Mercurial > hg-stable
changeset 30215:438173c41587 stable 4.0-rc
merge default into stable for 4.0 code freeze
author | Kevin Bullock <kbullock+mercurial@ringworld.org> |
---|---|
date | Tue, 18 Oct 2016 14:15:15 -0500 |
parents | fc11bb10425f (current diff) 260af19891f2 (diff) |
children | a314082946f8 |
files | hgext/largefiles/overrides.py tests/test-largefiles-update.t |
diffstat | 308 files changed, 8911 insertions(+), 3815 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CONTRIBUTING Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,13 @@ +Our full contribution guidelines are in our wiki, please see: + +https://www.mercurial-scm.org/wiki/ContributingChanges + +If you just want a checklist to follow, you can go straight to + +https://www.mercurial-scm.org/wiki/ContributingChanges#Submission_checklist + +If you can't run the entire testsuite for some reason (it can be +difficult on Windows), please at least run `contrib/check-code.py` on +any files you've modified and run `python contrib/check-commit` on any +commits you've made (for example, `python contrib/check-commit +273ce12ad8f1` will report some style violations on a very old commit).
--- a/Makefile Tue Oct 18 14:13:06 2016 -0500 +++ b/Makefile Tue Oct 18 14:15:15 2016 -0500 @@ -164,10 +164,12 @@ --install-lib=/Library/Python/2.7/site-packages/ make -C doc all install DESTDIR="$(PWD)/build/mercurial/" mkdir -p $${OUTPUTDIR:-dist} - pkgbuild --root build/mercurial/ --identifier org.mercurial-scm.mercurial \ - build/mercurial.pkg HGVER=$$((cat build/mercurial/Library/Python/2.7/site-packages/mercurial/__version__.py; echo 'print(version)') | python) && \ OSXVER=$$(sw_vers -productVersion | cut -d. -f1,2) && \ + pkgbuild --root build/mercurial/ \ + --identifier org.mercurial-scm.mercurial \ + --version "$${HGVER}" \ + build/mercurial.pkg && \ productbuild --distribution contrib/macosx/distribution.xml \ --package-path build/ \ --version "$${HGVER}" \
--- a/contrib/bash_completion Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/bash_completion Tue Oct 18 14:15:15 2016 -0500 @@ -89,9 +89,11 @@ _hg_status() { - local files="$(_hg_cmd status -n$1 "glob:$cur**")" - local IFS=$'\n' - COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) + if [ -z "$HGCOMPLETE_NOSTATUS" ]; then + local files="$(_hg_cmd status -n$1 "glob:$cur**")" + local IFS=$'\n' + COMPREPLY=(${COMPREPLY[@]:-} $(compgen -W '$files' -- "$cur")) + fi } _hg_branches()
--- a/contrib/check-code.py Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/check-code.py Tue Oct 18 14:15:15 2016 -0500 @@ -237,7 +237,7 @@ "tuple parameter unpacking not available in Python 3+"), (r'(?<!def)\s+(cmp)\(', "cmp is not available in Python 3+"), (r'\breduce\s*\(.*', "reduce is not available in Python 3+"), - (r'dict\(.*=', 'dict() is different in Py2 and 3 and is slower than {}', + (r'\bdict\(.*=', 'dict() is different in Py2 and 3 and is slower than {}', 'dict-from-generator'), (r'\.has_key\b', "dict.has_key is not available in Python 3+"), (r'\s<>\s', '<> operator is not available in Python 3+, use !='), @@ -295,7 +295,7 @@ "comparison with singleton, use 'is' or 'is not' instead"), (r'^\s*(while|if) [01]:', "use True/False for constant Boolean expression"), - (r'(?:(?<!def)\s+|\()hasattr', + (r'(?:(?<!def)\s+|\()hasattr\(', 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'), (r'opener\([^)]*\).read\(', "use opener.read() instead"),
--- a/contrib/check-commit Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/check-commit Tue Oct 18 14:15:15 2016 -0500 @@ -35,13 +35,18 @@ "summary line doesn't start with 'topic: '"), (afterheader + r"[A-Z][a-z]\S+", "don't capitalize summary lines"), (afterheader + r"[^\n]*: *[A-Z][a-z]\S+", "don't capitalize summary lines"), - (afterheader + r"\S*[^A-Za-z0-9-]\S*: ", + (afterheader + r"\S*[^A-Za-z0-9-_]\S*: ", "summary keyword should be most user-relevant one-word command or topic"), (afterheader + r".*\.\s*\n", "don't add trailing period on summary line"), (afterheader + r".{79,}", "summary line too long (limit is 78)"), (r"\n\+\n( |\+)\n", "adds double empty line"), (r"\n \n\+\n", "adds double empty line"), - (r"\n\+[ \t]+def [a-z]+_[a-z]", "adds a function with foo_bar naming"), + # Forbid "_" in function name. + # + # We skip the check for cffi related functions. They use names mapping the + # name of the C function. C function names may contain "_". + (r"\n\+[ \t]+def (?!cffi)[a-z]+_[a-z]", + "adds a function with foo_bar naming"), ] word = re.compile('\S')
--- a/contrib/check-py3-compat.py Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/check-py3-compat.py Tue Oct 18 14:15:15 2016 -0500 @@ -10,7 +10,6 @@ from __future__ import absolute_import, print_function import ast -import imp import os import sys import traceback @@ -41,6 +40,7 @@ def check_compat_py3(f): """Check Python 3 compatibility of a file with Python 3.""" + import importlib # not available on Python 2.6 with open(f, 'rb') as fh: content = fh.read() @@ -55,34 +55,33 @@ # out module paths for things not in a package can be confusing. if f.startswith(('hgext/', 'mercurial/')) and not f.endswith('__init__.py'): assert f.endswith('.py') - name = f.replace('/', '.')[:-3] - with open(f, 'r') as fh: - try: - imp.load_module(name, fh, '', ('py', 'r', imp.PY_SOURCE)) - except Exception as e: - exc_type, exc_value, tb = sys.exc_info() - # We walk the stack and ignore frames from our custom importer, - # import mechanisms, and stdlib modules. This kinda/sorta - # emulates CPython behavior in import.c while also attempting - # to pin blame on a Mercurial file. - for frame in reversed(traceback.extract_tb(tb)): - if frame.name == '_call_with_frames_removed': - continue - if 'importlib' in frame.filename: - continue - if 'mercurial/__init__.py' in frame.filename: - continue - if frame.filename.startswith(sys.prefix): - continue - break + name = f.replace('/', '.')[:-3].replace('.pure.', '.') + try: + importlib.import_module(name) + except Exception as e: + exc_type, exc_value, tb = sys.exc_info() + # We walk the stack and ignore frames from our custom importer, + # import mechanisms, and stdlib modules. This kinda/sorta + # emulates CPython behavior in import.c while also attempting + # to pin blame on a Mercurial file. + for frame in reversed(traceback.extract_tb(tb)): + if frame.name == '_call_with_frames_removed': + continue + if 'importlib' in frame.filename: + continue + if 'mercurial/__init__.py' in frame.filename: + continue + if frame.filename.startswith(sys.prefix): + continue + break - if frame.filename: - filename = os.path.basename(frame.filename) - print('%s: error importing: <%s> %s (error at %s:%d)' % ( - f, type(e).__name__, e, filename, frame.lineno)) - else: - print('%s: error importing module: <%s> %s (line %d)' % ( - f, type(e).__name__, e, frame.lineno)) + if frame.filename: + filename = os.path.basename(frame.filename) + print('%s: error importing: <%s> %s (error at %s:%d)' % ( + f, type(e).__name__, e, filename, frame.lineno)) + else: + print('%s: error importing module: <%s> %s (line %d)' % ( + f, type(e).__name__, e, frame.lineno)) if __name__ == '__main__': if sys.version_info[0] == 2:
--- a/contrib/chg/hgclient.c Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/chg/hgclient.c Tue Oct 18 14:15:15 2016 -0500 @@ -126,15 +126,10 @@ return; /* assumes input request */ size_t cursize = 0; - int emptycount = 0; while (cursize < hgc->ctx.datasize) { rsize = recv(hgc->sockfd, hgc->ctx.data + cursize, hgc->ctx.datasize - cursize, 0); - /* rsize == 0 normally indicates EOF, while it's also a valid - * packet size for unix socket. treat it as EOF and abort if - * we get many empty responses in a row. */ - emptycount = (rsize == 0 ? emptycount + 1 : 0); - if (rsize < 0 || emptycount > 20) + if (rsize < 1) abortmsg("failed to read data block"); cursize += rsize; }
--- a/contrib/perf.py Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/perf.py Tue Oct 18 14:15:15 2016 -0500 @@ -25,6 +25,7 @@ import sys import time from mercurial import ( + changegroup, cmdutil, commands, copies, @@ -130,15 +131,53 @@ # enforce an idle period before execution to counteract power management # experimental config: perf.presleep - time.sleep(ui.configint("perf", "presleep", 1)) + time.sleep(getint(ui, "perf", "presleep", 1)) if opts is None: opts = {} # redirect all to stderr ui = ui.copy() - ui.fout = ui.ferr + uifout = safeattrsetter(ui, 'fout', ignoremissing=True) + if uifout: + # for "historical portability": + # ui.fout/ferr have been available since 1.9 (or 4e1ccd4c2b6d) + uifout.set(ui.ferr) + # get a formatter - fm = ui.formatter('perf', opts) + uiformatter = getattr(ui, 'formatter', None) + if uiformatter: + fm = uiformatter('perf', opts) + else: + # for "historical portability": + # define formatter locally, because ui.formatter has been + # available since 2.2 (or ae5f92e154d3) + from mercurial import node + class defaultformatter(object): + """Minimized composition of baseformatter and plainformatter + """ + def __init__(self, ui, topic, opts): + self._ui = ui + if ui.debugflag: + self.hexfunc = node.hex + else: + self.hexfunc = node.short + def __nonzero__(self): + return False + def startitem(self): + pass + def data(self, **data): + pass + def write(self, fields, deftext, *fielddata, **opts): + self._ui.write(deftext % fielddata, **opts) + def condwrite(self, cond, fields, deftext, *fielddata, **opts): + if cond: + self._ui.write(deftext % fielddata, **opts) + def plain(self, text, **opts): + self._ui.write(text, **opts) + def end(self): + pass + fm = defaultformatter(ui, 'perf', opts) + # stub function, runs code only once instead of in a loop # experimental config: perf.stub if ui.configbool("perf", "stub"): @@ -181,6 +220,121 @@ fm.write('count', ' (best of %d)', count) fm.plain('\n') +# utilities for historical portability + +def getint(ui, section, name, default): + # for "historical portability": + # ui.configint has been available since 1.9 (or fa2b596db182) + v = ui.config(section, name, None) + if v is None: + return default + try: + return int(v) + except ValueError: + raise error.ConfigError(("%s.%s is not an integer ('%s')") + % (section, name, v)) + +def safeattrsetter(obj, name, ignoremissing=False): + """Ensure that 'obj' has 'name' attribute before subsequent setattr + + This function is aborted, if 'obj' doesn't have 'name' attribute + at runtime. This avoids overlooking removal of an attribute, which + breaks assumption of performance measurement, in the future. + + This function returns the object to (1) assign a new value, and + (2) restore an original value to the attribute. + + If 'ignoremissing' is true, missing 'name' attribute doesn't cause + abortion, and this function returns None. This is useful to + examine an attribute, which isn't ensured in all Mercurial + versions. + """ + if not util.safehasattr(obj, name): + if ignoremissing: + return None + raise error.Abort(("missing attribute %s of %s might break assumption" + " of performance measurement") % (name, obj)) + + origvalue = getattr(obj, name) + class attrutil(object): + def set(self, newvalue): + setattr(obj, name, newvalue) + def restore(self): + setattr(obj, name, origvalue) + + return attrutil() + +# utilities to examine each internal API changes + +def getbranchmapsubsettable(): + # for "historical portability": + # subsettable is defined in: + # - branchmap since 2.9 (or 175c6fd8cacc) + # - repoview since 2.5 (or 59a9f18d4587) + for mod in (branchmap, repoview): + subsettable = getattr(mod, 'subsettable', None) + if subsettable: + return subsettable + + # bisecting in bcee63733aad::59a9f18d4587 can reach here (both + # branchmap and repoview modules exist, but subsettable attribute + # doesn't) + raise error.Abort(("perfbranchmap not available with this Mercurial"), + hint="use 2.5 or later") + +def getsvfs(repo): + """Return appropriate object to access files under .hg/store + """ + # for "historical portability": + # repo.svfs has been available since 2.3 (or 7034365089bf) + svfs = getattr(repo, 'svfs', None) + if svfs: + return svfs + else: + return getattr(repo, 'sopener') + +def getvfs(repo): + """Return appropriate object to access files under .hg + """ + # for "historical portability": + # repo.vfs has been available since 2.3 (or 7034365089bf) + vfs = getattr(repo, 'vfs', None) + if vfs: + return vfs + else: + return getattr(repo, 'opener') + +def repocleartagscachefunc(repo): + """Return the function to clear tags cache according to repo internal API + """ + if util.safehasattr(repo, '_tagscache'): # since 2.0 (or 9dca7653b525) + # in this case, setattr(repo, '_tagscache', None) or so isn't + # correct way to clear tags cache, because existing code paths + # expect _tagscache to be a structured object. + def clearcache(): + # _tagscache has been filteredpropertycache since 2.5 (or + # 98c867ac1330), and delattr() can't work in such case + if '_tagscache' in vars(repo): + del repo.__dict__['_tagscache'] + return clearcache + + repotags = safeattrsetter(repo, '_tags', ignoremissing=True) + if repotags: # since 1.4 (or 5614a628d173) + return lambda : repotags.set(None) + + repotagscache = safeattrsetter(repo, 'tagscache', ignoremissing=True) + if repotagscache: # since 0.6 (or d7df759d0e97) + return lambda : repotagscache.set(None) + + # Mercurial earlier than 0.6 (or d7df759d0e97) logically reaches + # this point, but it isn't so problematic, because: + # - repo.tags of such Mercurial isn't "callable", and repo.tags() + # in perftags() causes failure soon + # - perf.py itself has been available since 1.1 (or eb240755386d) + raise error.Abort(("tags API of this hg command is unknown")) + +# perf commands + @command('perfwalk', formatteropts) def perfwalk(ui, repo, *pats, **opts): timer, fm = gettimer(ui, opts) @@ -249,10 +403,12 @@ import mercurial.changelog import mercurial.manifest timer, fm = gettimer(ui, opts) + svfs = getsvfs(repo) + repocleartagscache = repocleartagscachefunc(repo) def t(): - repo.changelog = mercurial.changelog.changelog(repo.svfs) - repo.manifest = mercurial.manifest.manifest(repo.svfs) - repo._tags = None + repo.changelog = mercurial.changelog.changelog(svfs) + repo.manifest = mercurial.manifest.manifest(svfs) + repocleartagscache() return len(repo.tags()) timer(t) fm.end() @@ -279,6 +435,37 @@ timer(d) fm.end() +@command('perfchangegroupchangelog', formatteropts + + [('', 'version', '02', 'changegroup version'), + ('r', 'rev', '', 'revisions to add to changegroup')]) +def perfchangegroupchangelog(ui, repo, version='02', rev=None, **opts): + """Benchmark producing a changelog group for a changegroup. + + This measures the time spent processing the changelog during a + bundle operation. This occurs during `hg bundle` and on a server + processing a `getbundle` wire protocol request (handles clones + and pull requests). + + By default, all revisions are added to the changegroup. + """ + cl = repo.changelog + revs = [cl.lookup(r) for r in repo.revs(rev or 'all()')] + bundler = changegroup.getbundler(version, repo) + + def lookup(node): + # The real bundler reads the revision in order to access the + # manifest node and files list. Do that here. + cl.read(node) + return node + + def d(): + for chunk in bundler.group(revs, cl, lookup): + pass + + timer, fm = gettimer(ui, opts) + timer(d) + fm.end() + @command('perfdirs', formatteropts) def perfdirs(ui, repo, **opts): timer, fm = gettimer(ui, opts) @@ -399,8 +586,9 @@ timer, fm = gettimer(ui, opts) mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg n = repo["tip"].node() + svfs = getsvfs(repo) def d(): - cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i") + cl = mercurial.revlog.revlog(svfs, "00changelog.i") cl.rev(n) timer(d) fm.end() @@ -423,7 +611,7 @@ timer, fm = gettimer(ui, opts) # control the number of commits perfparents iterates over # experimental config: perf.parentscount - count = ui.configint("perf", "parentscount", 1000) + count = getint(ui, "perf", "parentscount", 1000) if len(repo.changelog) < count: raise error.Abort("repo needs %d commits for this test" % count) repo = repo.unfiltered() @@ -472,7 +660,7 @@ import mercurial.revlog mercurial.revlog._prereadsize = 2**24 # disable lazy parser in old hg n = repo[rev].node() - cl = mercurial.revlog.revlog(repo.svfs, "00changelog.i") + cl = mercurial.revlog.revlog(getsvfs(repo), "00changelog.i") def d(): cl.rev(n) clearcaches(cl) @@ -543,8 +731,8 @@ s.fncache._dirty = True s.fncache.write(tr) timer(d) + tr.close() lock.release() - tr.close() fm.end() @command('perffncacheencode', formatteropts) @@ -580,9 +768,10 @@ @command('perfrevlog', revlogopts + formatteropts + [('d', 'dist', 100, 'distance between the revisions'), - ('s', 'startrev', 0, 'revision to start reading at')], + ('s', 'startrev', 0, 'revision to start reading at'), + ('', 'reverse', False, 'read in reverse')], '-c|-m|FILE') -def perfrevlog(ui, repo, file_=None, startrev=0, **opts): +def perfrevlog(ui, repo, file_=None, startrev=0, reverse=False, **opts): """Benchmark reading a series of revisions from a revlog. By default, we read every ``-d/--dist`` revision from 0 to tip of @@ -591,11 +780,20 @@ The start revision can be defined via ``-s/--startrev``. """ timer, fm = gettimer(ui, opts) - dist = opts['dist'] _len = getlen(ui) + def d(): r = cmdutil.openrevlog(repo, 'perfrevlog', file_, opts) - for x in xrange(startrev, _len(r), dist): + + startrev = 0 + endrev = _len(r) + dist = opts['dist'] + + if reverse: + startrev, endrev = endrev, startrev + dist = -1 * dist + + for x in xrange(startrev, endrev, dist): r.revision(r.node(x)) timer(d) @@ -772,10 +970,11 @@ return d # add filter in smaller subset to bigger subset possiblefilters = set(repoview.filtertable) + subsettable = getbranchmapsubsettable() allfilters = [] while possiblefilters: for name in possiblefilters: - subset = branchmap.subsettable.get(name) + subset = subsettable.get(name) if subset not in possiblefilters: break else: @@ -789,16 +988,17 @@ repo.filtered(name).branchmap() # add unfiltered allfilters.append(None) - oldread = branchmap.read - oldwrite = branchmap.branchcache.write + + branchcacheread = safeattrsetter(branchmap, 'read') + branchcachewrite = safeattrsetter(branchmap.branchcache, 'write') + branchcacheread.set(lambda repo: None) + branchcachewrite.set(lambda bc, repo: None) try: - branchmap.read = lambda repo: None - branchmap.write = lambda repo: None for name in allfilters: timer(getbranchmap(name), title=str(name)) finally: - branchmap.read = oldread - branchmap.branchcache.write = oldwrite + branchcacheread.restore() + branchcachewrite.restore() fm.end() @command('perfloadmarkers') @@ -807,7 +1007,8 @@ Result is the number of markers in the repo.""" timer, fm = gettimer(ui) - timer(lambda: len(obsolete.obsstore(repo.svfs))) + svfs = getsvfs(repo) + timer(lambda: len(obsolete.obsstore(svfs))) fm.end() @command('perflrucachedict', formatteropts +
--- a/contrib/synthrepo.py Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/synthrepo.py Tue Oct 18 14:15:15 2016 -0500 @@ -62,11 +62,11 @@ util, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' cmdtable = {} command = cmdutil.command(cmdtable)
--- a/contrib/wix/help.wxs Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/wix/help.wxs Tue Oct 18 14:15:15 2016 -0500 @@ -42,6 +42,7 @@ <File Id="internals.changegroups.txt" Name="changegroups.txt" /> <File Id="internals.requirements.txt" Name="requirements.txt" /> <File Id="internals.revlogs.txt" Name="revlogs.txt" /> + <File Id="internals.wireprotocol.txt" Name="wireprotocol.txt" /> </Component> </Directory>
--- a/contrib/zsh_completion Tue Oct 18 14:13:06 2016 -0500 +++ b/contrib/zsh_completion Tue Oct 18 14:15:15 2016 -0500 @@ -371,7 +371,7 @@ # Common options _hg_global_opts=( - '(--repository -R)'{-R+,--repository}'[repository root directory]:repository:_files -/' + '(--repository -R)'{-R+,--repository=}'[repository root directory]:repository:_files -/' '--cwd[change working directory]:new working directory:_files -/' '(--noninteractive -y)'{-y,--noninteractive}'[do not prompt, assume yes for any required answers]' '(--verbose -v)'{-v,--verbose}'[enable additional output]' @@ -390,8 +390,8 @@ ) _hg_pat_opts=( - '*'{-I+,--include}'[include names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/' - '*'{-X+,--exclude}'[exclude names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/') + '*'{-I+,--include=}'[include names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/' + '*'{-X+,--exclude=}'[exclude names matching the given patterns]:dir:_files -W $(_hg_cmd root) -/') _hg_clone_opts=( $_hg_remote_opts @@ -402,8 +402,8 @@ _hg_date_user_opts=( '(--currentdate -D)'{-D,--currentdate}'[record the current date as commit date]' '(--currentuser -U)'{-U,--currentuser}'[record the current user as committer]' - '(--date -d)'{-d+,--date}'[record the specified date as commit date]:date:' - '(--user -u)'{-u+,--user}'[record the specified user as committer]:user:') + '(--date -d)'{-d+,--date=}'[record the specified date as commit date]:date:' + '(--user -u)'{-u+,--user=}'[record the specified user as committer]:user:') _hg_gitlike_opts=( '(--git -g)'{-g,--git}'[use git extended diff format]') @@ -414,7 +414,7 @@ '--nodates[omit dates from diff headers]') _hg_mergetool_opts=( - '(--tool -t)'{-t+,--tool}'[specify merge tool]:tool:') + '(--tool -t)'{-t+,--tool=}'[specify merge tool]:tool:') _hg_dryrun_opts=( '(--dry-run -n)'{-n,--dry-run}'[do not perform actions, just print output]') @@ -430,7 +430,7 @@ _hg_log_opts=( $_hg_global_opts $_hg_style_opts $_hg_gitlike_opts - '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' + '(--limit -l)'{-l+,--limit=}'[limit number of changes displayed]:' '(--no-merges -M)'{-M,--no-merges}'[do not show merges]' '(--patch -p)'{-p,--patch}'[show patch]' '--stat[output diffstat-style summary of changes]' @@ -438,16 +438,16 @@ _hg_commit_opts=( '(-m --message -l --logfile --edit -e)'{-e,--edit}'[edit commit message]' - '(-e --edit -l --logfile --message -m)'{-m+,--message}'[use <text> as commit message]:message:' - '(-e --edit -m --message --logfile -l)'{-l+,--logfile}'[read the commit message from <file>]:log file:_files') + '(-e --edit -l --logfile --message -m)'{-m+,--message=}'[use <text> as commit message]:message:' + '(-e --edit -m --message --logfile -l)'{-l+,--logfile=}'[read the commit message from <file>]:log file:_files') _hg_remote_opts=( - '(--ssh -e)'{-e+,--ssh}'[specify ssh command to use]:' + '(--ssh -e)'{-e+,--ssh=}'[specify ssh command to use]:' '--remotecmd[specify hg command to run on the remote side]:') _hg_branch_bmark_opts=( - '(--bookmark -B)'{-B+,--bookmark}'[specify bookmark(s)]:bookmark:_hg_bookmarks' - '(--branch -b)'{-b+,--branch}'[specify branch(es)]:branch:_hg_branches' + '(--bookmark -B)'{-B+,--bookmark=}'[specify bookmark(s)]:bookmark:_hg_bookmarks' + '(--branch -b)'{-b+,--branch=}'[specify branch(es)]:branch:_hg_branches' ) _hg_subrepos_opts=( @@ -464,13 +464,13 @@ _hg_cmd_addremove() { _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \ - '(--similarity -s)'{-s+,--similarity}'[guess renamed files by similarity (0<=s<=100)]:' \ + '(--similarity -s)'{-s+,--similarity=}'[guess renamed files by similarity (0<=s<=100)]:' \ '*:unknown or missing files:_hg_addremove' } _hg_cmd_annotate() { _arguments -s -w : $_hg_global_opts $_hg_pat_opts \ - '(--rev -r)'{-r+,--rev}'[annotate the specified revision]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[annotate the specified revision]:revision:_hg_labels' \ '(--follow -f)'{-f,--follow}'[follow file copies and renames]' \ '(--text -a)'{-a,--text}'[treat all files as text]' \ '(--user -u)'{-u,--user}'[list the author]' \ @@ -483,21 +483,21 @@ _hg_cmd_archive() { _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_subrepos_opts \ '--no-decode[do not pass files through decoders]' \ - '(--prefix -p)'{-p+,--prefix}'[directory prefix for files in archive]:' \ - '(--rev -r)'{-r+,--rev}'[revision to distribute]:revision:_hg_labels' \ - '(--type -t)'{-t+,--type}'[type of distribution to create]:archive type:(files tar tbz2 tgz uzip zip)' \ + '(--prefix -p)'{-p+,--prefix=}'[directory prefix for files in archive]:' \ + '(--rev -r)'{-r+,--rev=}'[revision to distribute]:revision:_hg_labels' \ + '(--type -t)'{-t+,--type=}'[type of distribution to create]:archive type:(files tar tbz2 tgz uzip zip)' \ '*:destination:_files' } _hg_cmd_backout() { _arguments -s -w : $_hg_global_opts $_hg_mergetool_opts $_hg_pat_opts \ '--merge[merge with old dirstate parent after backout]' \ - '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \ + '(--date -d)'{-d+,--date=}'[record datecode as commit date]:date code:' \ '--parent[parent to choose when backing out merge]' \ - '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \ - '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \ - '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \ - '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_files' + '(--user -u)'{-u+,--user=}'[record user as commiter]:user:' \ + '(--rev -r)'{-r+,--rev=}'[revision]:revision:_hg_labels' \ + '(--message -m)'{-m+,--message=}'[use <text> as commit message]:text:' \ + '(--logfile -l)'{-l+,--logfile=}'[read commit message from <file>]:log file:_files' } _hg_cmd_bisect() { @@ -507,7 +507,7 @@ '(--good -g --bad -b --skip -s --reset -r)'{-g,--good}'[mark changeset good]'::revision:_hg_labels \ '(--good -g --bad -b --skip -s --reset -r)'{-b,--bad}'[mark changeset bad]'::revision:_hg_labels \ '(--good -g --bad -b --skip -s --reset -r)'{-s,--skip}'[skip testing changeset]' \ - '(--command -c --noupdate -U)'{-c+,--command}'[use command to check changeset state]':commands:_command_names \ + '(--command -c --noupdate -U)'{-c+,--command=}'[use command to check changeset state]':commands:_command_names \ '(--command -c --noupdate -U)'{-U,--noupdate}'[do not update to target]' } @@ -515,9 +515,9 @@ _arguments -s -w : $_hg_global_opts \ '(--force -f)'{-f,--force}'[force]' \ '(--inactive -i)'{-i,--inactive}'[mark a bookmark inactive]' \ - '(--rev -r --delete -d --rename -m)'{-r+,--rev}'[revision]:revision:_hg_labels' \ + '(--rev -r --delete -d --rename -m)'{-r+,--rev=}'[revision]:revision:_hg_labels' \ '(--rev -r --delete -d --rename -m)'{-d,--delete}'[delete a given bookmark]' \ - '(--rev -r --delete -d --rename -m)'{-m+,--rename}'[rename a given bookmark]:bookmark:_hg_bookmarks' \ + '(--rev -r --delete -d --rename -m)'{-m+,--rename=}'[rename a given bookmark]:bookmark:_hg_bookmarks' \ ':bookmark:_hg_bookmarks' } @@ -537,8 +537,8 @@ _arguments -s -w : $_hg_global_opts $_hg_remote_opts \ '(--force -f)'{-f,--force}'[run even when remote repository is unrelated]' \ '(2)*--base[a base changeset to specify instead of a destination]:revision:_hg_labels' \ - '(--branch -b)'{-b+,--branch}'[a specific branch to bundle]' \ - '(--rev -r)'{-r+,--rev}'[changeset(s) to bundle]:' \ + '(--branch -b)'{-b+,--branch=}'[a specific branch to bundle]:' \ + '(--rev -r)'{-r+,--rev=}'[changeset(s) to bundle]:' \ '--all[bundle all changesets in the repository]' \ ':output file:_files' \ ':destination repository:_files -/' @@ -546,17 +546,17 @@ _hg_cmd_cat() { _arguments -s -w : $_hg_global_opts $_hg_pat_opts \ - '(--output -o)'{-o+,--output}'[print output to file with formatted name]:filespec:' \ - '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \ + '(--output -o)'{-o+,--output=}'[print output to file with formatted name]:filespec:' \ + '(--rev -r)'{-r+,--rev=}'[revision]:revision:_hg_labels' \ '--decode[apply any matching decode filter]' \ '*:file:_hg_files' } _hg_cmd_clone() { _arguments -s -w : $_hg_global_opts $_hg_clone_opts \ - '(--rev -r)'{-r+,--rev}'[a changeset you would like to have after cloning]:' \ - '(--updaterev -u)'{-u+,--updaterev}'[revision, tag or branch to check out]' \ - '(--branch -b)'{-b+,--branch}'[clone only the specified branch]' \ + '(--rev -r)'{-r+,--rev=}'[a changeset you would like to have after cloning]:' \ + '(--updaterev -u)'{-u+,--updaterev=}'[revision, tag or branch to check out]:' \ + '(--branch -b)'{-b+,--branch=}'[clone only the specified branch]:' \ ':source repository:_hg_remote' \ ':destination:_hg_clone_dest' } @@ -564,10 +564,10 @@ _hg_cmd_commit() { _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_subrepos_opts \ '(--addremove -A)'{-A,--addremove}'[mark new/missing files as added/removed before committing]' \ - '(--message -m)'{-m+,--message}'[use <text> as commit message]:text:' \ - '(--logfile -l)'{-l+,--logfile}'[read commit message from <file>]:log file:_files' \ - '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \ - '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \ + '(--message -m)'{-m+,--message=}'[use <text> as commit message]:text:' \ + '(--logfile -l)'{-l+,--logfile=}'[read commit message from <file>]:log file:_files' \ + '(--date -d)'{-d+,--date=}'[record datecode as commit date]:date code:' \ + '(--user -u)'{-u+,--user=}'[record user as commiter]:user:' \ '--amend[amend the parent of the working dir]' \ '--close-branch[mark a branch as closed]' \ '*:file:_hg_files' @@ -584,12 +584,12 @@ typeset -A opt_args _arguments -s -w : $_hg_global_opts $_hg_diff_opts $_hg_ignore_space_opts \ $_hg_pat_opts $_hg_subrepos_opts \ - '*'{-r,--rev}'+[revision]:revision:_hg_revrange' \ + '*'{-r+,--rev=}'[revision]:revision:_hg_revrange' \ '(--show-function -p)'{-p,--show-function}'[show which function each change is in]' \ - '(--change -c)'{-c,--change}'[change made by revision]' \ + '(--change -c)'{-c+,--change=}'[change made by revision]:' \ '(--text -a)'{-a,--text}'[treat all files as text]' \ '--reverse[produce a diff that undoes the changes]' \ - '(--unified -U)'{-U,--unified}'[number of lines of context to show]' \ + '(--unified -U)'{-U+,--unified=}'[number of lines of context to show]:' \ '--stat[output diffstat-style summary of changes]' \ '*:file:->diff_files' @@ -606,9 +606,9 @@ _hg_cmd_export() { _arguments -s -w : $_hg_global_opts $_hg_diff_opts \ - '(--outout -o)'{-o+,--output}'[print output to file with formatted name]:filespec:' \ + '(--outout -o)'{-o+,--output=}'[print output to file with formatted name]:filespec:' \ '--switch-parent[diff against the second parent]' \ - '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[revision]:revision:_hg_labels' \ '*:revision:_hg_labels' } @@ -634,7 +634,7 @@ '(--ignore-case -i)'{-i,--ignore-case}'[ignore case when matching]' \ '(--files-with-matches -l)'{-l,--files-with-matches}'[print only filenames and revs that match]' \ '(--line-number -n)'{-n,--line-number}'[print matching line numbers]' \ - '*'{-r+,--rev}'[search in given revision range]:revision:_hg_revrange' \ + '*'{-r+,--rev=}'[search in given revision range]:revision:_hg_revrange' \ '(--user -u)'{-u,--user}'[print user who committed change]' \ '(--date -d)'{-d,--date}'[print date of a changeset]' \ '1:search pattern:' \ @@ -645,7 +645,7 @@ _arguments -s -w : $_hg_global_opts $_hg_style_opts \ '(--topo -t)'{-t,--topo}'[show topological heads only]' \ '(--closed -c)'{-c,--closed}'[show normal and closed branch heads]' \ - '(--rev -r)'{-r+,--rev}'[show only heads which are descendants of rev]:revision:_hg_labels' + '(--rev -r)'{-r+,--rev=}'[show only heads which are descendants of rev]:revision:_hg_labels' } _hg_cmd_help() { @@ -658,25 +658,25 @@ _hg_cmd_identify() { _arguments -s -w : $_hg_global_opts $_hg_remote_opts \ - '(--rev -r)'{-r+,--rev}'[identify the specified rev]:revision:_hg_labels' \ - '(--num -n)'{-n+,--num}'[show local revision number]' \ - '(--id -i)'{-i+,--id}'[show global revision id]' \ - '(--branch -b)'{-b+,--branch}'[show branch]' \ - '(--bookmark -B)'{-B+,--bookmark}'[show bookmarks]' \ - '(--tags -t)'{-t+,--tags}'[show tags]' + '(--rev -r)'{-r+,--rev=}'[identify the specified rev]:revision:_hg_labels' \ + '(--num -n)'{-n,--num}'[show local revision number]' \ + '(--id -i)'{-i,--id}'[show global revision id]' \ + '(--branch -b)'{-b,--branch}'[show branch]' \ + '(--bookmark -B)'{-B,--bookmark}'[show bookmarks]' \ + '(--tags -t)'{-t,--tags}'[show tags]' } _hg_cmd_import() { _arguments -s -w : $_hg_global_opts $_hg_commit_opts \ - '(--strip -p)'{-p+,--strip}'[directory strip option for patch (default: 1)]:count:' \ + '(--strip -p)'{-p+,--strip=}'[directory strip option for patch (default: 1)]:count:' \ '(--force -f)'{-f,--force}'[skip check for outstanding uncommitted changes]' \ '--bypass[apply patch without touching the working directory]' \ '--no-commit[do not commit, just update the working directory]' \ '--exact[apply patch to the nodes from which it was generated]' \ '--import-branch[use any branch information in patch (implied by --exact)]' \ - '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \ - '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \ - '(--similarity -s)'{-s+,--similarity}'[guess renamed files by similarity (0<=s<=100)]:' \ + '(--date -d)'{-d+,--date=}'[record datecode as commit date]:date code:' \ + '(--user -u)'{-u+,--user=}'[record user as commiter]:user:' \ + '(--similarity -s)'{-s+,--similarity=}'[guess renamed files by similarity (0<=s<=100)]:' \ '*:patch:_files' } @@ -684,7 +684,7 @@ _arguments -s -w : $_hg_log_opts $_hg_branch_bmark_opts $_hg_remote_opts \ $_hg_subrepos_opts \ '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \ - '(--rev -r)'{-r+,--rev}'[a specific revision up to which you would like to pull]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[a specific revision up to which you would like to pull]:revision:_hg_labels' \ '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \ '--bundle[file to store the bundles into]:bundle file:_files' \ ':source:_hg_remote' @@ -697,7 +697,7 @@ _hg_cmd_locate() { _arguments -s -w : $_hg_global_opts $_hg_pat_opts \ - '(--rev -r)'{-r+,--rev}'[search repository as it stood at revision]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[search repository as it stood at revision]:revision:_hg_labels' \ '(--print0 -0)'{-0,--print0}'[end filenames with NUL, for use with xargs]' \ '(--fullpath -f)'{-f,--fullpath}'[print complete paths]' \ '*:search pattern:_hg_files' @@ -709,27 +709,27 @@ '(-f --follow)--follow-first[only follow the first parent of merge changesets]' \ '(--copies -C)'{-C,--copies}'[show copied files]' \ '(--keyword -k)'{-k+,--keyword}'[search for a keyword]:' \ - '*'{-r,--rev}'[show the specified revision or revset]:revision:_hg_revrange' \ + '*'{-r+,--rev=}'[show the specified revision or revset]:revision:_hg_revrange' \ '(--only-merges -m)'{-m,--only-merges}'[show only merges]' \ - '(--prune -P)'{-P+,--prune}'[do not display revision or any of its ancestors]:revision:_hg_labels' \ - '(--graph -G)'{-G+,--graph}'[show the revision DAG]' \ - '(--branch -b)'{-b+,--branch}'[show changesets within the given named branch]:branch:_hg_branches' \ - '(--user -u)'{-u+,--user}'[revisions committed by user]:user:' \ - '(--date -d)'{-d+,--date}'[show revisions matching date spec]:date:' \ + '(--prune -P)'{-P+,--prune=}'[do not display revision or any of its ancestors]:revision:_hg_labels' \ + '(--graph -G)'{-G,--graph}'[show the revision DAG]' \ + '(--branch -b)'{-b+,--branch=}'[show changesets within the given named branch]:branch:_hg_branches' \ + '(--user -u)'{-u+,--user=}'[revisions committed by user]:user:' \ + '(--date -d)'{-d+,--date=}'[show revisions matching date spec]:date:' \ '*:files:_hg_files' } _hg_cmd_manifest() { _arguments -s -w : $_hg_global_opts \ '--all[list files from all revisions]' \ - '(--rev -r)'{-r+,--rev}'[revision to display]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[revision to display]:revision:_hg_labels' \ ':revision:_hg_labels' } _hg_cmd_merge() { _arguments -s -w : $_hg_global_opts $_hg_mergetool_opts \ '(--force -f)'{-f,--force}'[force a merge with outstanding changes]' \ - '(--rev -r 1)'{-r,--rev}'[revision to merge]:revision:_hg_mergerevs' \ + '(--rev -r 1)'{-r+,--rev=}'[revision to merge]:revision:_hg_mergerevs' \ '(--preview -P)'{-P,--preview}'[review revisions to merge (no merge is performed)]' \ ':revision:_hg_mergerevs' } @@ -738,14 +738,14 @@ _arguments -s -w : $_hg_log_opts $_hg_branch_bmark_opts $_hg_remote_opts \ $_hg_subrepos_opts \ '(--force -f)'{-f,--force}'[run even when the remote repository is unrelated]' \ - '*'{-r,--rev}'[a specific revision you would like to push]:revision:_hg_revrange' \ + '*'{-r+,--rev=}'[a specific revision you would like to push]:revision:_hg_revrange' \ '(--newest-first -n)'{-n,--newest-first}'[show newest record first]' \ ':destination:_hg_remote' } _hg_cmd_parents() { _arguments -s -w : $_hg_global_opts $_hg_style_opts \ - '(--rev -r)'{-r+,--rev}'[show parents of the specified rev]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[show parents of the specified rev]:revision:_hg_labels' \ ':last modified file:_hg_files' } @@ -760,7 +760,7 @@ '(--draft -d)'{-d,--draft}'[set changeset phase to draft]' \ '(--secret -s)'{-s,--secret}'[set changeset phase to secret]' \ '(--force -f)'{-f,--force}'[allow to move boundary backward]' \ - '(--rev -r)'{-r+,--rev}'[target revision]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[target revision]:revision:_hg_labels' \ ':revision:_hg_labels' } @@ -775,7 +775,7 @@ _hg_cmd_push() { _arguments -s -w : $_hg_global_opts $_hg_branch_bmark_opts $_hg_remote_opts \ '(--force -f)'{-f,--force}'[force push]' \ - '(--rev -r)'{-r+,--rev}'[a specific revision you would like to push]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[a specific revision you would like to push]:revision:_hg_labels' \ '--new-branch[allow pushing a new branch]' \ ':destination:_hg_remote' } @@ -819,9 +819,9 @@ _arguments -s -w : $_hg_global_opts $_hg_pat_opts $_hg_dryrun_opts \ '(--all -a :)'{-a,--all}'[revert all changes when no arguments given]' \ - '(--rev -r)'{-r+,--rev}'[revision to revert to]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[revision to revert to]:revision:_hg_labels' \ '(--no-backup -C)'{-C,--no-backup}'[do not save backup copies of files]' \ - '(--date -d)'{-d+,--date}'[tipmost revision matching date]:date code:' \ + '(--date -d)'{-d+,--date=}'[tipmost revision matching date]:date code:' \ '*:file:->diff_files' if [[ $state == 'diff_files' ]] @@ -844,13 +844,13 @@ _hg_cmd_serve() { _arguments -s -w : $_hg_global_opts \ - '(--accesslog -A)'{-A+,--accesslog}'[name of access log file]:log file:_files' \ - '(--errorlog -E)'{-E+,--errorlog}'[name of error log file]:log file:_files' \ + '(--accesslog -A)'{-A+,--accesslog=}'[name of access log file]:log file:_files' \ + '(--errorlog -E)'{-E+,--errorlog=}'[name of error log file]:log file:_files' \ '(--daemon -d)'{-d,--daemon}'[run server in background]' \ - '(--port -p)'{-p+,--port}'[listen port]:listen port:' \ - '(--address -a)'{-a+,--address}'[interface address]:interface address:' \ + '(--port -p)'{-p+,--port=}'[listen port]:listen port:' \ + '(--address -a)'{-a+,--address=}'[interface address]:interface address:' \ '--prefix[prefix path to serve from]:directory:_files' \ - '(--name -n)'{-n+,--name}'[name to show in web pages]:repository name:' \ + '(--name -n)'{-n+,--name=}'[name to show in web pages]:repository name:' \ '--web-conf[name of the hgweb config file]:webconf_file:_files' \ '--pid-file[name of file to write process ID to]:pid_file:_files' \ '--cmdserver[cmdserver mode]:mode:' \ @@ -863,7 +863,7 @@ _hg_cmd_showconfig() { _arguments -s -w : $_hg_global_opts \ - '(--untrusted -u)'{-u+,--untrusted}'[show untrusted configuration options]' \ + '(--untrusted -u)'{-u,--untrusted}'[show untrusted configuration options]' \ ':config item:_hg_config' } @@ -893,10 +893,10 @@ _hg_cmd_tag() { _arguments -s -w : $_hg_global_opts \ '(--local -l)'{-l,--local}'[make the tag local]' \ - '(--message -m)'{-m+,--message}'[message for tag commit log entry]:message:' \ - '(--date -d)'{-d+,--date}'[record datecode as commit date]:date code:' \ - '(--user -u)'{-u+,--user}'[record user as commiter]:user:' \ - '(--rev -r)'{-r+,--rev}'[revision to tag]:revision:_hg_labels' \ + '(--message -m)'{-m+,--message=}'[message for tag commit log entry]:message:' \ + '(--date -d)'{-d+,--date=}'[record datecode as commit date]:date code:' \ + '(--user -u)'{-u+,--user=}'[record user as commiter]:user:' \ + '(--rev -r)'{-r+,--rev=}'[revision to tag]:revision:_hg_labels' \ '(--force -f)'{-f,--force}'[force tag]' \ '--remove[remove a tag]' \ '(--edit -e)'{-e,--edit}'[edit commit message]' \ @@ -917,9 +917,9 @@ _hg_cmd_update() { _arguments -s -w : $_hg_global_opts \ '(--clean -C)'{-C,--clean}'[overwrite locally modified files]' \ - '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \ + '(--rev -r)'{-r+,--rev=}'[revision]:revision:_hg_labels' \ '(--check -c)'{-c,--check}'[update across branches if no uncommitted changes]' \ - '(--date -d)'{-d+,--date}'[tipmost revision matching date]' \ + '(--date -d)'{-d+,--date=}'[tipmost revision matching date]:' \ ':revision:_hg_labels' } @@ -928,7 +928,7 @@ # HGK _hg_cmd_view() { _arguments -s -w : $_hg_global_opts \ - '(--limit -l)'{-l+,--limit}'[limit number of changes displayed]:' \ + '(--limit -l)'{-l+,--limit=}'[limit number of changes displayed]:' \ ':revision range:_hg_labels' } @@ -989,7 +989,7 @@ _hg_cmd_qclone() { _arguments -s -w : $_hg_global_opts $_hg_remote_opts $_hg_clone_opts \ - '(--patches -p)'{-p+,--patches}'[location of source patch repository]' \ + '(--patches -p)'{-p+,--patches=}'[location of source patch repository]:' \ ':source repository:_hg_remote' \ ':destination:_hg_clone_dest' } @@ -997,7 +997,7 @@ _hg_cmd_qdelete() { _arguments -s -w : $_hg_global_opts \ '(--keep -k)'{-k,--keep}'[keep patch file]' \ - '*'{-r+,--rev}'[stop managing a revision]:applied patch:_hg_revrange' \ + '*'{-r+,--rev=}'[stop managing a revision]:applied patch:_hg_revrange' \ '*:unapplied patch:_hg_qdeletable' } @@ -1046,7 +1046,7 @@ '(--existing -e)'{-e,--existing}'[import file in patch dir]' \ '(--name -n 2)'{-n+,--name}'[patch file name]:name:' \ '(--force -f)'{-f,--force}'[overwrite existing files]' \ - '*'{-r+,--rev}'[place existing revisions under mq control]:revision:_hg_revrange' \ + '*'{-r+,--rev=}'[place existing revisions under mq control]:revision:_hg_revrange' \ '(--push -P)'{-P,--push}'[qpush after importing]' \ '*:patch:_files' } @@ -1125,8 +1125,8 @@ '(--force -f)'{-f,--force}'[force removal, discard uncommitted changes, no backup]' \ '(--no-backup -n)'{-n,--no-backup}'[no backups]' \ '(--keep -k)'{-k,--keep}'[do not modify working copy during strip]' \ - '(--bookmark -B)'{-B+,--bookmark}'[remove revs only reachable from given bookmark]:bookmark:_hg_bookmarks' \ - '(--rev -r)'{-r+,--rev}'[revision]:revision:_hg_labels' \ + '(--bookmark -B)'{-B+,--bookmark=}'[remove revs only reachable from given bookmark]:bookmark:_hg_bookmarks' \ + '(--rev -r)'{-r+,--rev=}'[revision]:revision:_hg_labels' \ ':revision:_hg_labels' } @@ -1138,7 +1138,7 @@ '(--outgoing -o)'{-o,--outgoing}'[send changes not found in the target repository]' \ '(--bundle -b)'{-b,--bundle}'[send changes not in target as a binary bundle]' \ '--bundlename[name of the bundle attachment file (default: bundle)]:' \ - '*'{-r+,--rev}'[search in given revision range]:revision:_hg_revrange' \ + '*'{-r+,--rev=}'[search in given revision range]:revision:_hg_revrange' \ '--force[run even when remote repository is unrelated (with -b/--bundle)]' \ '*--base[a base changeset to specify instead of a destination (with -b/--bundle)]:revision:_hg_labels' \ '--intro[send an introduction email for a single patch]' \ @@ -1163,10 +1163,10 @@ # Rebase _hg_cmd_rebase() { _arguments -s -w : $_hg_global_opts $_hg_commit_opts $_hg_mergetool_opts \ - '*'{-r,--rev}'[rebase these revisions]:revision:_hg_revrange' \ - '(--source -s)'{-s+,--source}'[rebase from the specified changeset]:revision:_hg_labels' \ - '(--base -b)'{-b+,--base}'[rebase from the base of the specified changeset]:revision:_hg_labels' \ - '(--dest -d)'{-d+,--dest}'[rebase onto the specified changeset]:revision:_hg_labels' \ + '*'{-r+,--rev=}'[rebase these revisions]:revision:_hg_revrange' \ + '(--source -s)'{-s+,--source=}'[rebase from the specified changeset]:revision:_hg_labels' \ + '(--base -b)'{-b+,--base=}'[rebase from the base of the specified changeset]:revision:_hg_labels' \ + '(--dest -d)'{-d+,--dest=}'[rebase onto the specified changeset]:revision:_hg_labels' \ '--collapse[collapse the rebased changeset]' \ '--keep[keep original changeset]' \ '--keepbranches[keep original branch name]' \ @@ -1181,8 +1181,8 @@ '(--addremove -A)'{-A,--addremove}'[mark new/missing files as added/removed before committing]' \ '--close-branch[mark a branch as closed, hiding it from the branch list]' \ '--amend[amend the parent of the working dir]' \ - '(--date -d)'{-d+,--date}'[record the specified date as commit date]:date:' \ - '(--user -u)'{-u+,--user}'[record the specified user as committer]:user:' + '(--date -d)'{-d+,--date=}'[record the specified date as commit date]:date:' \ + '(--user -u)'{-u+,--user=}'[record the specified user as committer]:user:' } _hg_cmd_qrecord() { @@ -1195,8 +1195,8 @@ _arguments -s -w : $_hg_global_opts \ '(--source-type -s)'{-s,--source-type}'[source repository type]' \ '(--dest-type -d)'{-d,--dest-type}'[destination repository type]' \ - '(--rev -r)'{-r+,--rev}'[import up to target revision]:revision:' \ - '(--authormap -A)'{-A+,--authormap}'[remap usernames using this file]:file:_files' \ + '(--rev -r)'{-r+,--rev=}'[import up to target revision]:revision:' \ + '(--authormap -A)'{-A+,--authormap=}'[remap usernames using this file]:file:_files' \ '--filemap[remap file names using contents of file]:file:_files' \ '--splicemap[splice synthesized history into place]:file:_files' \ '--branchmap[change branch names while converting]:file:_files' \
--- a/doc/hgmanpage.py Tue Oct 18 14:13:06 2016 -0500 +++ b/doc/hgmanpage.py Tue Oct 18 14:15:15 2016 -0500 @@ -57,7 +57,6 @@ import roman except ImportError: from docutils.utils import roman -import inspect FIELD_LIST_INDENT = 7 DEFINITION_LIST_INDENT = 7 @@ -289,10 +288,10 @@ text = node.astext() text = text.replace('\\','\\e') replace_pairs = [ - (u'-', ur'\-'), - (u'\'', ur'\(aq'), - (u'´', ur'\''), - (u'`', ur'\(ga'), + (u'-', u'\\-'), + (u"'", u'\\(aq'), + (u'´', u"\\'"), + (u'`', u'\\(ga'), ] for (in_char, out_markup) in replace_pairs: text = text.replace(in_char, out_markup)
--- a/hgext/acl.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/acl.py Tue Oct 18 14:15:15 2016 -0500 @@ -204,11 +204,11 @@ urlreq = util.urlreq -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def _getusers(ui, group):
--- a/hgext/blackbox.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/blackbox.py Tue Oct 18 14:15:15 2016 -0500 @@ -51,11 +51,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' lastui = None filehandles = {}
--- a/hgext/bugzilla.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/bugzilla.py Tue Oct 18 14:15:15 2016 -0500 @@ -294,11 +294,11 @@ urlparse = util.urlparse xmlrpclib = util.xmlrpclib -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' class bzaccess(object): '''Base class for access to Bugzilla.'''
--- a/hgext/censor.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/censor.py Tue Oct 18 14:15:15 2016 -0500 @@ -42,11 +42,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command('censor', [('r', 'rev', '', _('censor file from specified revision'), _('REV')),
--- a/hgext/chgserver.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/chgserver.py Tue Oct 18 14:15:15 2016 -0500 @@ -63,11 +63,11 @@ util, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' _log = commandserver.log
--- a/hgext/children.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/children.py Tue Oct 18 14:15:15 2016 -0500 @@ -26,11 +26,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command('children', [('r', 'rev', '',
--- a/hgext/churn.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/churn.py Tue Oct 18 14:15:15 2016 -0500 @@ -26,11 +26,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def maketemplater(ui, repo, tmpl): return cmdutil.changeset_templater(ui, repo, False, None, tmpl, None, False)
--- a/hgext/clonebundles.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/clonebundles.py Tue Oct 18 14:15:15 2016 -0500 @@ -169,7 +169,7 @@ wireproto, ) -testedwith = 'internal' +testedwith = 'ships-with-hg-core' def capabilities(orig, repo, proto): caps = orig(repo, proto)
--- a/hgext/color.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/color.py Tue Oct 18 14:15:15 2016 -0500 @@ -29,6 +29,15 @@ Some may not be available for a given terminal type, and will be silently ignored. +If the terminfo entry for your terminal is missing codes for an effect +or has the wrong codes, you can add or override those codes in your +configuration:: + + [color] + terminfo.dim = \E[2m + +where '\E' is substituted with an escape character. + Labels ------ @@ -170,11 +179,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # start and stop parameters for effects _effects = {'none': 0, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, @@ -196,9 +205,12 @@ if mode not in ('auto', 'terminfo'): return - _terminfo_params.update((key[6:], (False, int(val))) + _terminfo_params.update((key[6:], (False, int(val), '')) for key, val in ui.configitems('color') if key.startswith('color.')) + _terminfo_params.update((key[9:], (True, '', val.replace('\\E', '\x1b'))) + for key, val in ui.configitems('color') + if key.startswith('terminfo.')) try: curses.setupterm() @@ -206,10 +218,10 @@ _terminfo_params = {} return - for key, (b, e) in _terminfo_params.items(): + for key, (b, e, c) in _terminfo_params.items(): if not b: continue - if not curses.tigetstr(e): + if not c and not curses.tigetstr(e): # Most terminals don't support dim, invis, etc, so don't be # noisy and use ui.debug(). ui.debug("no terminfo entry for %s\n" % e) @@ -290,26 +302,26 @@ try: import curses - # Mapping from effect name to terminfo attribute name or color number. - # This will also force-load the curses module. - _terminfo_params = {'none': (True, 'sgr0'), - 'standout': (True, 'smso'), - 'underline': (True, 'smul'), - 'reverse': (True, 'rev'), - 'inverse': (True, 'rev'), - 'blink': (True, 'blink'), - 'dim': (True, 'dim'), - 'bold': (True, 'bold'), - 'invisible': (True, 'invis'), - 'italic': (True, 'sitm'), - 'black': (False, curses.COLOR_BLACK), - 'red': (False, curses.COLOR_RED), - 'green': (False, curses.COLOR_GREEN), - 'yellow': (False, curses.COLOR_YELLOW), - 'blue': (False, curses.COLOR_BLUE), - 'magenta': (False, curses.COLOR_MAGENTA), - 'cyan': (False, curses.COLOR_CYAN), - 'white': (False, curses.COLOR_WHITE)} + # Mapping from effect name to terminfo attribute name (or raw code) or + # color number. This will also force-load the curses module. + _terminfo_params = {'none': (True, 'sgr0', ''), + 'standout': (True, 'smso', ''), + 'underline': (True, 'smul', ''), + 'reverse': (True, 'rev', ''), + 'inverse': (True, 'rev', ''), + 'blink': (True, 'blink', ''), + 'dim': (True, 'dim', ''), + 'bold': (True, 'bold', ''), + 'invisible': (True, 'invis', ''), + 'italic': (True, 'sitm', ''), + 'black': (False, curses.COLOR_BLACK, ''), + 'red': (False, curses.COLOR_RED, ''), + 'green': (False, curses.COLOR_GREEN, ''), + 'yellow': (False, curses.COLOR_YELLOW, ''), + 'blue': (False, curses.COLOR_BLUE, ''), + 'magenta': (False, curses.COLOR_MAGENTA, ''), + 'cyan': (False, curses.COLOR_CYAN, ''), + 'white': (False, curses.COLOR_WHITE, '')} except ImportError: _terminfo_params = {} @@ -375,9 +387,15 @@ if effect.endswith('_background'): bg = True effect = effect[:-11] - attr, val = _terminfo_params[effect] + try: + attr, val, termcode = _terminfo_params[effect] + except KeyError: + return '' if attr: - return curses.tigetstr(val) + if termcode: + return termcode + else: + return curses.tigetstr(val) elif bg: return curses.tparm(curses.tigetstr('setab'), val) else: @@ -412,7 +430,7 @@ def configstyles(ui): for status, cfgeffects in ui.configitems('color'): - if '.' not in status or status.startswith('color.'): + if '.' not in status or status.startswith(('color.', 'terminfo.')): continue cfgeffects = ui.configlist('color', status) if cfgeffects: @@ -524,10 +542,16 @@ _styles = {} for effect in _effects.keys(): _styles[effect] = effect + if _terminfo_params: + for k, v in ui.configitems('color'): + if k.startswith('color.'): + _styles[k] = k[6:] + elif k.startswith('terminfo.'): + _styles[k] = k[9:] ui.write(('color mode: %s\n') % ui._colormode) ui.write(_('available colors:\n')) - for label, colors in _styles.items(): - ui.write(('%s\n') % colors, label=label) + for colorname, label in _styles.items(): + ui.write(('%s\n') % colorname, label=label) if os.name != 'nt': w32effects = None @@ -558,8 +582,8 @@ ('srWindow', _SMALL_RECT), ('dwMaximumWindowSize', _COORD)] - _STD_OUTPUT_HANDLE = 0xfffffff5L # (DWORD)-11 - _STD_ERROR_HANDLE = 0xfffffff4L # (DWORD)-12 + _STD_OUTPUT_HANDLE = 0xfffffff5 # (DWORD)-11 + _STD_ERROR_HANDLE = 0xfffffff4 # (DWORD)-12 _FOREGROUND_BLUE = 0x0001 _FOREGROUND_GREEN = 0x0002
--- a/hgext/convert/__init__.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/convert/__init__.py Tue Oct 18 14:15:15 2016 -0500 @@ -23,11 +23,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # Commands definition was moved elsewhere to ease demandload job.
--- a/hgext/convert/subversion.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/convert/subversion.py Tue Oct 18 14:15:15 2016 -0500 @@ -531,8 +531,8 @@ def checkrevformat(self, revstr, mapname='splicemap'): """ fails if revision format does not match the correct format""" if not re.match(r'svn:[0-9a-f]{8,8}-[0-9a-f]{4,4}-' - '[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]' - '{12,12}(.*)\@[0-9]+$',revstr): + r'[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]' + r'{12,12}(.*)\@[0-9]+$',revstr): raise error.Abort(_('%s entry %s is not a valid revision' ' identifier') % (mapname, revstr))
--- a/hgext/eol.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/eol.py Tue Oct 18 14:15:15 2016 -0500 @@ -104,11 +104,11 @@ util, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # Matches a lone LF, i.e., one that is not part of CRLF. singlelf = re.compile('(^|[^\r])\n') @@ -175,25 +175,27 @@ include = [] exclude = [] + self.patterns = [] for pattern, style in self.cfg.items('patterns'): key = style.upper() if key == 'BIN': exclude.append(pattern) else: include.append(pattern) + m = match.match(root, '', [pattern]) + self.patterns.append((pattern, key, m)) # This will match the files for which we need to care # about inconsistent newlines. self.match = match.match(root, '', [], include, exclude) def copytoui(self, ui): - for pattern, style in self.cfg.items('patterns'): - key = style.upper() + for pattern, key, m in self.patterns: try: ui.setconfig('decode', pattern, self._decode[key], 'eol') ui.setconfig('encode', pattern, self._encode[key], 'eol') except KeyError: ui.warn(_("ignoring unknown EOL style '%s' from %s\n") - % (style, self.cfg.source('patterns', pattern))) + % (key, self.cfg.source('patterns', pattern))) # eol.only-consistent can be specified in ~/.hgrc or .hgeol for k, v in self.cfg.items('eol'): ui.setconfig('eol', k, v, 'eol') @@ -203,10 +205,10 @@ for f in (files or ctx.files()): if f not in ctx: continue - for pattern, style in self.cfg.items('patterns'): - if not match.match(repo.root, '', [pattern])(f): + for pattern, key, m in self.patterns: + if not m(f): continue - target = self._encode[style.upper()] + target = self._encode[key] data = ctx[f].data() if (target == "to-lf" and "\r\n" in data or target == "to-crlf" and singlelf.search(data)): @@ -305,15 +307,20 @@ return eol.match def _hgcleardirstate(self): - self._eolfile = self.loadeol([None, 'tip']) - if not self._eolfile: - self._eolfile = util.never + self._eolmatch = self.loadeol([None, 'tip']) + if not self._eolmatch: + self._eolmatch = util.never return + oldeol = None try: cachemtime = os.path.getmtime(self.join("eol.cache")) except OSError: cachemtime = 0 + else: + olddata = self.vfs.read("eol.cache") + if olddata: + oldeol = eolfile(self.ui, self.root, olddata) try: eolmtime = os.path.getmtime(self.wjoin(".hgeol")) @@ -322,18 +329,37 @@ if eolmtime > cachemtime: self.ui.debug("eol: detected change in .hgeol\n") + + hgeoldata = self.wvfs.read('.hgeol') + neweol = eolfile(self.ui, self.root, hgeoldata) + wlock = None try: wlock = self.wlock() for f in self.dirstate: - if self.dirstate[f] == 'n': - # all normal files need to be looked at - # again since the new .hgeol file might no - # longer match a file it matched before - self.dirstate.normallookup(f) - # Create or touch the cache to update mtime - self.vfs("eol.cache", "w").close() - wlock.release() + if self.dirstate[f] != 'n': + continue + if oldeol is not None: + if not oldeol.match(f) and not neweol.match(f): + continue + oldkey = None + for pattern, key, m in oldeol.patterns: + if m(f): + oldkey = key + break + newkey = None + for pattern, key, m in neweol.patterns: + if m(f): + newkey = key + break + if oldkey == newkey: + continue + # all normal files need to be looked at again since + # the new .hgeol file specify a different filter + self.dirstate.normallookup(f) + # Write the cache to update mtime and cache .hgeol + with self.vfs("eol.cache", "w") as f: + f.write(hgeoldata) except error.LockUnavailable: # If we cannot lock the repository and clear the # dirstate, then a commit might not see all files @@ -341,10 +367,13 @@ # repository, then we can also not make a commit, # so ignore the error. pass + finally: + if wlock is not None: + wlock.release() def commitctx(self, ctx, haserror=False): for f in sorted(ctx.added() + ctx.modified()): - if not self._eolfile(f): + if not self._eolmatch(f): continue fctx = ctx[f] if fctx is None:
--- a/hgext/extdiff.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/extdiff.py Tue Oct 18 14:15:15 2016 -0500 @@ -84,11 +84,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def snapshot(ui, repo, files, node, tmproot, listsubrepos): '''snapshot files as of some revision @@ -324,6 +324,34 @@ cmdline = ' '.join(map(util.shellquote, [program] + option)) return dodiff(ui, repo, cmdline, pats, opts) +class savedcmd(object): + """use external program to diff repository (or selected files) + + Show differences between revisions for the specified files, using + the following program:: + + %(path)s + + When two revision arguments are given, then changes are shown + between those revisions. If only one revision is specified then + that revision is compared to the working directory, and, when no + revisions are specified, the working directory files are compared + to its parent. + """ + + def __init__(self, path, cmdline): + # We can't pass non-ASCII through docstrings (and path is + # in an unknown encoding anyway) + docpath = path.encode("string-escape") + self.__doc__ = self.__doc__ % {'path': util.uirepr(docpath)} + self._cmdline = cmdline + + def __call__(self, ui, repo, *pats, **opts): + options = ' '.join(map(util.shellquote, opts['option'])) + if options: + options = ' ' + options + return dodiff(ui, repo, self._cmdline + options, pats, opts) + def uisetup(ui): for cmd, path in ui.configitems('extdiff'): path = util.expandpath(path) @@ -357,28 +385,8 @@ ui.config('merge-tools', cmd+'.diffargs') if args: cmdline += ' ' + args - def save(cmdline): - '''use closure to save diff command to use''' - def mydiff(ui, repo, *pats, **opts): - options = ' '.join(map(util.shellquote, opts['option'])) - if options: - options = ' ' + options - return dodiff(ui, repo, cmdline + options, pats, opts) - # We can't pass non-ASCII through docstrings (and path is - # in an unknown encoding anyway) - docpath = path.encode("string-escape") - mydiff.__doc__ = '''\ -use %(path)s to diff repository (or selected files) + command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd, + inferrepo=True)(savedcmd(path, cmdline)) - Show differences between revisions for the specified files, using - the %(path)s program. - - When two revision arguments are given, then changes are shown - between those revisions. If only one revision is specified then - that revision is compared to the working directory, and, when no - revisions are specified, the working directory files are compared - to its parent.\ -''' % {'path': util.uirepr(docpath)} - return mydiff - command(cmd, extdiffopts[:], _('hg %s [OPTION]... [FILE]...') % cmd, - inferrepo=True)(save(cmdline)) +# tell hggettext to extract docstrings from these functions: +i18nfunctions = [savedcmd]
--- a/hgext/fetch.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/fetch.py Tue Oct 18 14:15:15 2016 -0500 @@ -26,11 +26,11 @@ release = lock.release cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command('fetch', [('r', 'rev', [],
--- a/hgext/fsmonitor/__init__.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/fsmonitor/__init__.py Tue Oct 18 14:15:15 2016 -0500 @@ -113,11 +113,11 @@ watchmanclient, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # This extension is incompatible with the following blacklisted extensions # and will disable itself when encountering one of these:
--- a/hgext/gpg.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/gpg.py Tue Oct 18 14:15:15 2016 -0500 @@ -23,11 +23,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' class gpg(object): def __init__(self, path, key=None):
--- a/hgext/graphlog.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/graphlog.py Tue Oct 18 14:15:15 2016 -0500 @@ -25,11 +25,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command('glog', [('f', 'follow', None,
--- a/hgext/hgk.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/hgk.py Tue Oct 18 14:15:15 2016 -0500 @@ -54,11 +54,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command('debug-diff-tree', [('p', 'patch', None, _('generate patch')),
--- a/hgext/highlight/__init__.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/highlight/__init__.py Tue Oct 18 14:15:15 2016 -0500 @@ -41,11 +41,11 @@ fileset, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def pygmentize(web, field, fctx, tmpl): style = web.config('web', 'pygments_style', 'colorful')
--- a/hgext/histedit.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/histedit.py Tue Oct 18 14:15:15 2016 -0500 @@ -201,23 +201,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -class _constraints(object): - # aborts if there are multiple rules for one node - noduplicates = 'noduplicates' - # abort if the node does belong to edited stack - forceother = 'forceother' - # abort if the node doesn't belong to edited stack - noother = 'noother' - - @classmethod - def known(cls): - return set([v for k, v in cls.__dict__.items() if k[0] != '_']) - -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' actiontable = {} primaryactions = set() @@ -403,7 +391,7 @@ raise error.ParseError("invalid changeset %s" % rulehash) return cls(state, rev) - def verify(self, prev): + def verify(self, prev, expected, seen): """ Verifies semantic correctness of the rule""" repo = self.repo ha = node.hex(self.node) @@ -412,6 +400,19 @@ except error.RepoError: raise error.ParseError(_('unknown changeset %s listed') % ha[:12]) + if self.node is not None: + self._verifynodeconstraints(prev, expected, seen) + + def _verifynodeconstraints(self, prev, expected, seen): + # by default command need a node in the edited list + if self.node not in expected: + raise error.ParseError(_('%s "%s" changeset was not a candidate') + % (self.verb, node.short(self.node)), + hint=_('only use listed changesets')) + # and only one command per node + if self.node in seen: + raise error.ParseError(_('duplicated command for changeset %s') % + node.short(self.node)) def torule(self): """build a histedit rule line for an action @@ -434,19 +435,6 @@ """ return "%s\n%s" % (self.verb, node.hex(self.node)) - def constraints(self): - """Return a set of constrains that this action should be verified for - """ - return set([_constraints.noduplicates, _constraints.noother]) - - def nodetoverify(self): - """Returns a node associated with the action that will be used for - verification purposes. - - If the action doesn't correspond to node it should return None - """ - return self.node - def run(self): """Runs the action. The default behavior is simply apply the action's rulectx onto the current parentctx.""" @@ -573,18 +561,7 @@ copied = copies.pathcopies(base, last) # prune files which were reverted by the updates - def samefile(f): - if f in last.manifest(): - a = last.filectx(f) - if f in base.manifest(): - b = base.filectx(f) - return (a.data() == b.data() - and a.flags() == b.flags()) - else: - return False - else: - return f not in base.manifest() - files = [f for f in files if not samefile(f)] + files = [f for f in files if not cmdutil.samefile(f, last, base)] # commit version of these files as defined by head headmf = last.manifest() def filectxfn(repo, ctx, path): @@ -683,9 +660,9 @@ @action(['fold', 'f'], _('use commit, but combine it with the one above')) class fold(histeditaction): - def verify(self, prev): + def verify(self, prev, expected, seen): """ Verifies semantic correctness of the fold rule""" - super(fold, self).verify(prev) + super(fold, self).verify(prev, expected, seen) repo = self.repo if not prev: c = repo[self.node].parents()[0] @@ -795,8 +772,6 @@ return repo[n], replacements class base(histeditaction): - def constraints(self): - return set([_constraints.forceother]) def run(self): if self.repo['.'].node() != self.node: @@ -811,6 +786,14 @@ basectx = self.repo['.'] return basectx, [] + def _verifynodeconstraints(self, prev, expected, seen): + # base can only be use with a node not in the edited set + if self.node in expected: + msg = _('%s "%s" changeset was an edited list candidate') + raise error.ParseError( + msg % (self.verb, node.short(self.node)), + hint=_('base must only use unlisted changesets')) + @action(['_multifold'], _( """fold subclass used for when multiple folds happen in a row @@ -871,7 +854,7 @@ roots = list(repo.revs("roots(%ln)", outgoing.missing)) if 1 < len(roots): msg = _('there are ambiguous outgoing revisions') - hint = _('see "hg help histedit" for more detail') + hint = _("see 'hg help histedit' for more detail") raise error.Abort(msg, hint=hint) return repo.lookup(roots[0]) @@ -1210,8 +1193,8 @@ else: rules = _readfile(rules) actions = parserules(rules, state) - ctxs = [repo[act.nodetoverify()] \ - for act in state.actions if act.nodetoverify()] + ctxs = [repo[act.node] \ + for act in state.actions if act.node] warnverifyactions(ui, repo, actions, state, ctxs) state.actions = actions state.write() @@ -1307,7 +1290,7 @@ root = ctxs[0] # list is already sorted by repo.set if not root.mutable(): raise error.Abort(_('cannot edit public changeset: %s') % root, - hint=_('see "hg help phases" for details')) + hint=_("see 'hg help phases' for details")) return [c.node() for c in ctxs] def ruleeditor(repo, ui, actions, editcomment=""): @@ -1396,36 +1379,14 @@ Will abort if there are to many or too few rules, a malformed rule, or a rule on a changeset outside of the user-given range. """ - expected = set(c.hex() for c in ctxs) + expected = set(c.node() for c in ctxs) seen = set() prev = None for action in actions: - action.verify(prev) + action.verify(prev, expected, seen) prev = action - constraints = action.constraints() - for constraint in constraints: - if constraint not in _constraints.known(): - raise error.ParseError(_('unknown constraint "%s"') % - constraint) - - nodetoverify = action.nodetoverify() - if nodetoverify is not None: - ha = node.hex(nodetoverify) - if _constraints.noother in constraints and ha not in expected: - raise error.ParseError( - _('%s "%s" changeset was not a candidate') - % (action.verb, ha[:12]), - hint=_('only use listed changesets')) - if _constraints.forceother in constraints and ha in expected: - raise error.ParseError( - _('%s "%s" changeset was not an edited list candidate') - % (action.verb, ha[:12]), - hint=_('only use listed changesets')) - if _constraints.noduplicates in constraints and ha in seen: - raise error.ParseError(_( - 'duplicated command for changeset %s') % - ha[:12]) - seen.add(ha) + if action.node is not None: + seen.add(action.node) missing = sorted(expected - seen) # sort to stabilize output if state.repo.ui.configbool('histedit', 'dropmissing'): @@ -1433,15 +1394,16 @@ raise error.ParseError(_('no rules provided'), hint=_('use strip extension to remove commits')) - drops = [drop(state, node.bin(n)) for n in missing] + drops = [drop(state, n) for n in missing] # put the in the beginning so they execute immediately and # don't show in the edit-plan in the future actions[:0] = drops elif missing: raise error.ParseError(_('missing rules for changeset %s') % - missing[0][:12], + node.short(missing[0]), hint=_('use "drop %s" to discard, see also: ' - '"hg help -e histedit.config"') % missing[0][:12]) + "'hg help -e histedit.config'") + % node.short(missing[0])) def adjustreplacementsfrommarkers(repo, oldreplacements): """Adjust replacements from obsolescense markers @@ -1608,10 +1570,9 @@ if os.path.exists(os.path.join(repo.path, 'histedit-state')): state = histeditstate(repo) state.read() - histedit_nodes = set([action.nodetoverify() for action - in state.actions if action.nodetoverify()]) - strip_nodes = set([repo[n].node() for n in nodelist]) - common_nodes = histedit_nodes & strip_nodes + histedit_nodes = set([action.node for action + in state.actions if action.node]) + common_nodes = histedit_nodes & set(nodelist) if common_nodes: raise error.Abort(_("histedit in progress, can't strip %s") % ', '.join(node.short(x) for x in common_nodes))
--- a/hgext/journal.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/journal.py Tue Oct 18 14:15:15 2016 -0500 @@ -24,7 +24,6 @@ bookmarks, cmdutil, commands, - dirstate, dispatch, error, extensions, @@ -40,11 +39,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # storage format version; increment when the format changes storageversion = 0 @@ -63,8 +62,6 @@ extensions.wrapfunction(dispatch, 'runcommand', runcommand) extensions.wrapfunction(bookmarks.bmstore, '_write', recordbookmarks) extensions.wrapfunction( - dirstate.dirstate, '_writedirstate', recorddirstateparents) - extensions.wrapfunction( localrepo.localrepository.dirstate, 'func', wrapdirstate) extensions.wrapfunction(hg, 'postshare', wrappostshare) extensions.wrapfunction(hg, 'copystore', unsharejournal) @@ -84,34 +81,19 @@ dirstate = orig(repo) if util.safehasattr(repo, 'journal'): dirstate.journalstorage = repo.journal + dirstate.addparentchangecallback('journal', recorddirstateparents) return dirstate -def recorddirstateparents(orig, dirstate, dirstatefp): +def recorddirstateparents(dirstate, old, new): """Records all dirstate parent changes in the journal.""" + old = list(old) + new = list(new) if util.safehasattr(dirstate, 'journalstorage'): - old = [node.nullid, node.nullid] - nodesize = len(node.nullid) - try: - # The only source for the old state is in the dirstate file still - # on disk; the in-memory dirstate object only contains the new - # state. dirstate._opendirstatefile() switches beteen .hg/dirstate - # and .hg/dirstate.pending depending on the transaction state. - with dirstate._opendirstatefile() as fp: - state = fp.read(2 * nodesize) - if len(state) == 2 * nodesize: - old = [state[:nodesize], state[nodesize:]] - except IOError: - pass - - new = dirstate.parents() - if old != new: - # only record two hashes if there was a merge - oldhashes = old[:1] if old[1] == node.nullid else old - newhashes = new[:1] if new[1] == node.nullid else new - dirstate.journalstorage.record( - wdirparenttype, '.', oldhashes, newhashes) - - return orig(dirstate, dirstatefp) + # only record two hashes if there was a merge + oldhashes = old[:1] if old[1] == node.nullid else old + newhashes = new[:1] if new[1] == node.nullid else new + dirstate.journalstorage.record( + wdirparenttype, '.', oldhashes, newhashes) # hooks to record bookmark changes (both local and remote) def recordbookmarks(orig, store, fp): @@ -165,9 +147,10 @@ def wrappostshare(orig, sourcerepo, destrepo, **kwargs): """Mark this shared working copy as sharing journal information""" - orig(sourcerepo, destrepo, **kwargs) - with destrepo.vfs('shared', 'a') as fp: - fp.write('journal\n') + with destrepo.wlock(): + orig(sourcerepo, destrepo, **kwargs) + with destrepo.vfs('shared', 'a') as fp: + fp.write('journal\n') def unsharejournal(orig, ui, repo, repopath): """Copy shared journal entries into this repo when unsharing""" @@ -179,12 +162,12 @@ # there is a shared repository and there are shared journal entries # to copy. move shared date over from source to destination but # move the local file first - if repo.vfs.exists('journal'): - journalpath = repo.join('journal') + if repo.vfs.exists('namejournal'): + journalpath = repo.join('namejournal') util.rename(journalpath, journalpath + '.bak') storage = repo.journal local = storage._open( - repo.vfs, filename='journal.bak', _newestfirst=False) + repo.vfs, filename='namejournal.bak', _newestfirst=False) shared = ( e for e in storage._open(sharedrepo.vfs, _newestfirst=False) if sharednamespaces.get(e.namespace) in sharedfeatures) @@ -194,8 +177,8 @@ return orig(ui, repo, repopath) class journalentry(collections.namedtuple( - 'journalentry', - 'timestamp user command namespace name oldhashes newhashes')): + u'journalentry', + u'timestamp user command namespace name oldhashes newhashes')): """Individual journal entry * timestamp: a mercurial (time, timezone) tuple @@ -284,19 +267,31 @@ # with a non-local repo (cloning for example). cls._currentcommand = fullargs + def _currentlock(self, lockref): + """Returns the lock if it's held, or None if it's not. + + (This is copied from the localrepo class) + """ + if lockref is None: + return None + l = lockref() + if l is None or not l.held: + return None + return l + def jlock(self, vfs): """Create a lock for the journal file""" - if self._lockref and self._lockref(): + if self._currentlock(self._lockref) is not None: raise error.Abort(_('journal lock does not support nesting')) desc = _('journal of %s') % vfs.base try: - l = lock.lock(vfs, 'journal.lock', 0, desc=desc) + l = lock.lock(vfs, 'namejournal.lock', 0, desc=desc) except error.LockHeld as inst: self.ui.warn( _("waiting for lock on %s held by %r\n") % (desc, inst.locker)) # default to 600 seconds timeout l = lock.lock( - vfs, 'journal.lock', + vfs, 'namejournal.lock', int(self.ui.config("ui", "timeout", "600")), desc=desc) self.ui.warn(_("got lock after %s seconds\n") % l.delay) self._lockref = weakref.ref(l) @@ -336,7 +331,7 @@ with self.jlock(vfs): version = None # open file in amend mode to ensure it is created if missing - with vfs('journal', mode='a+b', atomictemp=True) as f: + with vfs('namejournal', 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) @@ -394,7 +389,7 @@ if sharednamespaces.get(e.namespace) in self.sharedfeatures) return _mergeentriesiter(local, shared) - def _open(self, vfs, filename='journal', _newestfirst=True): + def _open(self, vfs, filename='namejournal', _newestfirst=True): if not vfs.exists(filename): return @@ -475,8 +470,10 @@ for count, entry in enumerate(repo.journal.filtered(name=name)): if count == limit: break - newhashesstr = ','.join([node.short(hash) for hash in entry.newhashes]) - oldhashesstr = ','.join([node.short(hash) for hash in entry.oldhashes]) + newhashesstr = fm.formatlist(map(fm.hexfunc, entry.newhashes), + name='node', sep=',') + oldhashesstr = fm.formatlist(map(fm.hexfunc, entry.oldhashes), + name='node', sep=',') fm.startitem() fm.condwrite(ui.verbose, 'oldhashes', '%s -> ', oldhashesstr) @@ -486,7 +483,7 @@ opts.get('all') or name.startswith('re:'), 'name', ' %-8s', entry.name) - timestring = util.datestr(entry.timestamp, '%Y-%m-%d %H:%M %1%2') + timestring = fm.formatdate(entry.timestamp, '%Y-%m-%d %H:%M %1%2') fm.condwrite(ui.verbose, 'date', ' %s', timestring) fm.write('command', ' %s\n', entry.command)
--- a/hgext/keyword.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/keyword.py Tue Oct 18 14:15:15 2016 -0500 @@ -24,7 +24,7 @@ # Files to act upon/ignore are specified in the [keyword] section. # Customized keyword template mappings in the [keywordmaps] section. # -# Run "hg help keyword" and "hg kwdemo" to get info on configuration. +# Run 'hg help keyword' and 'hg kwdemo' to get info on configuration. '''expand keywords in tracked files @@ -112,11 +112,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # hg commands that do not act on keywords nokwcommands = ('add addremove annotate bundle export grep incoming init log'
--- a/hgext/largefiles/__init__.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/largefiles/__init__.py Tue Oct 18 14:15:15 2016 -0500 @@ -119,11 +119,11 @@ uisetup as uisetupmod, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' reposetup = reposetup.reposetup
--- a/hgext/largefiles/basestore.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/largefiles/basestore.py Tue Oct 18 14:15:15 2016 -0500 @@ -91,15 +91,13 @@ storefilename = lfutil.storepath(self.repo, hash) tmpname = storefilename + '.tmp' - tmpfile = util.atomictempfile(tmpname, - createmode=self.repo.store.createmode) - - try: - gothash = self._getfile(tmpfile, filename, hash) - except StoreError as err: - self.ui.warn(err.longmessage()) - gothash = "" - tmpfile.close() + with util.atomictempfile(tmpname, + createmode=self.repo.store.createmode) as tmpfile: + try: + gothash = self._getfile(tmpfile, filename, hash) + except StoreError as err: + self.ui.warn(err.longmessage()) + gothash = "" if gothash != hash: if gothash != "":
--- a/hgext/largefiles/lfcommands.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/largefiles/lfcommands.py Tue Oct 18 14:15:15 2016 -0500 @@ -515,9 +515,13 @@ rellfile = lfile relstandin = lfutil.standin(lfile) if wvfs.exists(relstandin): - mode = wvfs.stat(relstandin).st_mode - if mode != wvfs.stat(rellfile).st_mode: - wvfs.chmod(rellfile, mode) + standinexec = wvfs.stat(relstandin).st_mode & 0o100 + st = wvfs.stat(rellfile).st_mode + if standinexec != st & 0o100: + st &= ~0o111 + if standinexec: + st |= (st >> 2) & 0o111 & ~util.umask + wvfs.chmod(rellfile, st) update1 = 1 updated += update1
--- a/hgext/largefiles/lfutil.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/largefiles/lfutil.py Tue Oct 18 14:15:15 2016 -0500 @@ -54,10 +54,10 @@ util.oslink(src, dest) except OSError: # if hardlinks fail, fallback on atomic copy - dst = util.atomictempfile(dest) - for chunk in util.filechunkiter(open(src, 'rb')): - dst.write(chunk) - dst.close() + with open(src, 'rb') as srcf: + with util.atomictempfile(dest) as dstf: + for chunk in util.filechunkiter(srcf): + dstf.write(chunk) os.chmod(dest, os.stat(src).st_mode) def usercachepath(ui, hash): @@ -231,7 +231,8 @@ # don't use atomic writes in the working copy. with open(path, 'rb') as srcfd: with wvfs(filename, 'wb') as destfd: - gothash = copyandhash(srcfd, destfd) + gothash = copyandhash( + util.filechunkiter(srcfd), destfd) if gothash != hash: repo.ui.warn(_('%s: data corruption in %s with hash %s\n') % (filename, path, gothash)) @@ -264,11 +265,11 @@ link(usercachepath(repo.ui, hash), storepath(repo, hash)) else: util.makedirs(os.path.dirname(storepath(repo, hash))) - dst = util.atomictempfile(storepath(repo, hash), - createmode=repo.store.createmode) - for chunk in util.filechunkiter(open(file, 'rb')): - dst.write(chunk) - dst.close() + with open(file, 'rb') as srcf: + with util.atomictempfile(storepath(repo, hash), + createmode=repo.store.createmode) as dstf: + for chunk in util.filechunkiter(srcf): + dstf.write(chunk) linktousercache(repo, hash) def linktousercache(repo, hash): @@ -370,10 +371,9 @@ if not os.path.exists(file): return '' hasher = hashlib.sha1('') - fd = open(file, 'rb') - for data in util.filechunkiter(fd, 128 * 1024): - hasher.update(data) - fd.close() + with open(file, 'rb') as fd: + for data in util.filechunkiter(fd): + hasher.update(data) return hasher.hexdigest() def getexecutable(filename):
--- a/hgext/largefiles/localstore.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/largefiles/localstore.py Tue Oct 18 14:15:15 2016 -0500 @@ -10,6 +10,7 @@ from __future__ import absolute_import from mercurial.i18n import _ +from mercurial import util from . import ( basestore, @@ -42,7 +43,8 @@ raise basestore.StoreError(filename, hash, self.url, _("can't get file locally")) with open(path, 'rb') as fd: - return lfutil.copyandhash(fd, tmpfile) + return lfutil.copyandhash( + util.filechunkiter(fd), tmpfile) def _verifyfiles(self, contents, filestocheck): failed = False
--- a/hgext/largefiles/overrides.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/largefiles/overrides.py Tue Oct 18 14:15:15 2016 -0500 @@ -883,11 +883,8 @@ # If largefiles is required for this repo, permanently enable it locally if 'largefiles' in repo.requirements: - fp = repo.vfs('hgrc', 'a', text=True) - try: + with repo.vfs('hgrc', 'a', text=True) as fp: fp.write('\n[extensions]\nlargefiles=\n') - finally: - fp.close() # Caching is implicitly limited to 'rev' option, since the dest repo was # truncated at that point. The user may expect a download count with @@ -1339,30 +1336,28 @@ m.visitdir = lfvisitdirfn for f in ctx.walk(m): - fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(), - pathname=f) - lf = lfutil.splitstandin(f) - if lf is None or origmatchfn(f): - # duplicating unreachable code from commands.cat - data = ctx[f].data() - if opts.get('decode'): - data = repo.wwritedata(f, data) - fp.write(data) - else: - hash = lfutil.readstandin(repo, lf, ctx.rev()) - if not lfutil.inusercache(repo.ui, hash): - store = storefactory.openstore(repo) - success, missing = store.get([(lf, hash)]) - if len(success) != 1: - raise error.Abort( - _('largefile %s is not in cache and could not be ' - 'downloaded') % lf) - path = lfutil.usercachepath(repo.ui, hash) - fpin = open(path, "rb") - for chunk in util.filechunkiter(fpin, 128 * 1024): - fp.write(chunk) - fpin.close() - fp.close() + with cmdutil.makefileobj(repo, opts.get('output'), ctx.node(), + pathname=f) as fp: + lf = lfutil.splitstandin(f) + if lf is None or origmatchfn(f): + # duplicating unreachable code from commands.cat + data = ctx[f].data() + if opts.get('decode'): + data = repo.wwritedata(f, data) + fp.write(data) + else: + hash = lfutil.readstandin(repo, lf, ctx.rev()) + if not lfutil.inusercache(repo.ui, hash): + store = storefactory.openstore(repo) + success, missing = store.get([(lf, hash)]) + if len(success) != 1: + raise error.Abort( + _('largefile %s is not in cache and could not be ' + 'downloaded') % lf) + path = lfutil.usercachepath(repo.ui, hash) + with open(path, "rb") as fpin: + for chunk in util.filechunkiter(fpin): + fp.write(chunk) err = 0 return err
--- a/hgext/largefiles/proto.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/largefiles/proto.py Tue Oct 18 14:15:15 2016 -0500 @@ -134,7 +134,7 @@ length)) # SSH streams will block if reading more than length - for chunk in util.filechunkiter(stream, 128 * 1024, length): + for chunk in util.filechunkiter(stream, limit=length): yield chunk # HTTP streams must hit the end to process the last empty # chunk of Chunked-Encoding so the connection can be reused.
--- a/hgext/largefiles/remotestore.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/largefiles/remotestore.py Tue Oct 18 14:15:15 2016 -0500 @@ -45,17 +45,13 @@ def sendfile(self, filename, hash): self.ui.debug('remotestore: sendfile(%s, %s)\n' % (filename, hash)) - fd = None try: - fd = lfutil.httpsendfile(self.ui, filename) - return self._put(hash, fd) + with lfutil.httpsendfile(self.ui, filename) as fd: + return self._put(hash, fd) except IOError as e: raise error.Abort( _('remotestore: could not open file %s: %s') % (filename, str(e))) - finally: - if fd: - fd.close() def _getfile(self, tmpfile, filename, hash): try: @@ -122,7 +118,7 @@ raise NotImplementedError('abstract method') def _get(self, hash): - '''Get file with the given hash from the remote store.''' + '''Get a iterator for content with the given hash.''' raise NotImplementedError('abstract method') def _stat(self, hashes):
--- a/hgext/logtoprocess.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/logtoprocess.py Tue Oct 18 14:15:15 2016 -0500 @@ -40,11 +40,11 @@ import subprocess import sys -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def uisetup(ui): if platform.system() == 'Windows':
--- a/hgext/mq.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/mq.py Tue Oct 18 14:15:15 2016 -0500 @@ -99,11 +99,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # force load strip extension formerly included in mq and import some utility try: @@ -1562,7 +1562,7 @@ if not repo[self.applied[-1].node].mutable(): raise error.Abort( _("popping would remove a public revision"), - hint=_('see "hg help phases" for details')) + hint=_("see 'hg help phases' for details")) # we know there are no local changes, so we can make a simplified # form of hg.update. @@ -1631,7 +1631,7 @@ raise error.Abort(_("cannot qrefresh a revision with children")) if not repo[top].mutable(): raise error.Abort(_("cannot qrefresh public revision"), - hint=_('see "hg help phases" for details')) + hint=_("see 'hg help phases' for details")) cparents = repo.changelog.parents(top) patchparent = self.qparents(repo, top) @@ -1840,7 +1840,7 @@ self.applied.append(statusentry(n, patchfn)) finally: - lockmod.release(lock, tr) + lockmod.release(tr, lock) except: # re-raises ctx = repo[cparents[0]] repo.dirstate.rebuild(ctx.node(), ctx.manifest()) @@ -2117,7 +2117,7 @@ for r in rev: if not repo[r].mutable(): raise error.Abort(_('revision %d is not mutable') % r, - hint=_('see "hg help phases" ' + hint=_("see 'hg help phases' " 'for details')) p1, p2 = repo.changelog.parentrevs(r) n = repo.changelog.node(r) @@ -3354,53 +3354,54 @@ raise error.Abort( _('invalid queue name, may not contain the characters ":\\/."')) - existing = _getqueues() - - if opts.get('create'): - if name in existing: - raise error.Abort(_('queue "%s" already exists') % name) - if _noqueues(): - _addqueue(_defaultqueue) - _addqueue(name) - _setactive(name) - elif opts.get('rename'): - current = _getcurrent() - if name == current: - raise error.Abort(_('can\'t rename "%s" to its current name') - % name) - if name in existing: - raise error.Abort(_('queue "%s" already exists') % name) - - olddir = _queuedir(current) - newdir = _queuedir(name) - - if os.path.exists(newdir): - raise error.Abort(_('non-queue directory "%s" already exists') % - newdir) - - fh = repo.vfs('patches.queues.new', 'w') - for queue in existing: - if queue == current: - fh.write('%s\n' % (name,)) - if os.path.exists(olddir): - util.rename(olddir, newdir) - else: - fh.write('%s\n' % (queue,)) - fh.close() - util.rename(repo.join('patches.queues.new'), repo.join(_allqueues)) - _setactivenocheck(name) - elif opts.get('delete'): - _delete(name) - elif opts.get('purge'): - if name in existing: + with repo.wlock(): + existing = _getqueues() + + if opts.get('create'): + if name in existing: + raise error.Abort(_('queue "%s" already exists') % name) + if _noqueues(): + _addqueue(_defaultqueue) + _addqueue(name) + _setactive(name) + elif opts.get('rename'): + current = _getcurrent() + if name == current: + raise error.Abort(_('can\'t rename "%s" to its current name') + % name) + if name in existing: + raise error.Abort(_('queue "%s" already exists') % name) + + olddir = _queuedir(current) + newdir = _queuedir(name) + + if os.path.exists(newdir): + raise error.Abort(_('non-queue directory "%s" already exists') % + newdir) + + fh = repo.vfs('patches.queues.new', 'w') + for queue in existing: + if queue == current: + fh.write('%s\n' % (name,)) + if os.path.exists(olddir): + util.rename(olddir, newdir) + else: + fh.write('%s\n' % (queue,)) + fh.close() + util.rename(repo.join('patches.queues.new'), repo.join(_allqueues)) + _setactivenocheck(name) + elif opts.get('delete'): _delete(name) - qdir = _queuedir(name) - if os.path.exists(qdir): - shutil.rmtree(qdir) - else: - if name not in existing: - raise error.Abort(_('use --create to create a new queue')) - _setactive(name) + elif opts.get('purge'): + if name in existing: + _delete(name) + qdir = _queuedir(name) + if os.path.exists(qdir): + shutil.rmtree(qdir) + else: + if name not in existing: + raise error.Abort(_('use --create to create a new queue')) + _setactive(name) def mqphasedefaults(repo, roots): """callback used to set mq changeset as secret when no phase data exists"""
--- a/hgext/notify.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/notify.py Tue Oct 18 14:15:15 2016 -0500 @@ -148,11 +148,11 @@ util, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # template for single changeset can include email headers. single_template = '''
--- a/hgext/pager.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/pager.py Tue Oct 18 14:15:15 2016 -0500 @@ -10,7 +10,7 @@ # [extension] # pager = # -# Run "hg help pager" to get info on configuration. +# Run 'hg help pager' to get info on configuration. '''browse command output with an external pager @@ -75,11 +75,11 @@ util, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def _runpager(ui, p): pager = subprocess.Popen(p, shell=True, bufsize=-1,
--- a/hgext/patchbomb.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/patchbomb.py Tue Oct 18 14:15:15 2016 -0500 @@ -87,11 +87,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def _addpullheader(seq, ctx): """Add a header pointing to a public URL where the changeset is available
--- a/hgext/purge.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/purge.py Tue Oct 18 14:15:15 2016 -0500 @@ -38,11 +38,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command('purge|clean', [('a', 'abort-on-err', None, _('abort if an error occurs')),
--- a/hgext/rebase.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/rebase.py Tue Oct 18 14:15:15 2016 -0500 @@ -66,11 +66,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def _nothingtorebase(): return 1 @@ -296,7 +296,7 @@ if not self.keepf and not self.repo[root].mutable(): raise error.Abort(_("can't rebase public changeset %s") % self.repo[root], - hint=_('see "hg help phases" for details')) + hint=_("see 'hg help phases' for details")) (self.originalwd, self.target, self.state) = result if self.collapsef: @@ -335,8 +335,9 @@ if self.activebookmark: bookmarks.deactivate(repo) - sortedrevs = sorted(self.state) - total = len(self.state) + sortedrevs = repo.revs('sort(%ld, -topo)', self.state) + cands = [k for k, v in self.state.iteritems() if v == revtodo] + total = len(cands) pos = 0 for rev in sortedrevs: ctx = repo[rev] @@ -345,8 +346,8 @@ names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node()) if names: desc += ' (%s)' % ' '.join(names) - pos += 1 if self.state[rev] == revtodo: + pos += 1 ui.status(_('rebasing %s\n') % desc) ui.progress(_("rebasing"), pos, ("%d:%s" % (rev, ctx)), _('changesets'), total) @@ -1127,7 +1128,7 @@ if immutable: repo.ui.warn(_("warning: can't clean up public changesets %s\n") % ', '.join(str(repo[r]) for r in immutable), - hint=_('see "hg help phases" for details')) + hint=_("see 'hg help phases' for details")) cleanup = False descendants = set() @@ -1197,7 +1198,7 @@ repo.ui.debug('source is a child of destination\n') return None - repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root)) + repo.ui.debug('rebase onto %s starting from %s\n' % (dest, root)) state.update(dict.fromkeys(rebaseset, revtodo)) # Rebase tries to turn <dest> into a parent of <root> while # preserving the number of parents of rebased changesets:
--- a/hgext/record.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/record.py Tue Oct 18 14:15:15 2016 -0500 @@ -22,11 +22,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command("record", @@ -70,7 +70,7 @@ backup = ui.backupconfig('experimental', 'crecord') try: ui.setconfig('experimental', 'crecord', False, 'record') - commands.commit(ui, repo, *pats, **opts) + return commands.commit(ui, repo, *pats, **opts) finally: ui.restoreconfig(backup)
--- a/hgext/relink.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/relink.py Tue Oct 18 14:15:15 2016 -0500 @@ -21,11 +21,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command('relink', [], _('[ORIGIN]')) def relink(ui, repo, origin=None, **opts):
--- a/hgext/schemes.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/schemes.py Tue Oct 18 14:15:15 2016 -0500 @@ -56,11 +56,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' class ShortRepository(object):
--- a/hgext/share.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/share.py Tue Oct 18 14:15:15 2016 -0500 @@ -56,11 +56,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' @command('share', [('U', 'noupdate', None, _('do not create a working directory')),
--- a/hgext/shelve.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/shelve.py Tue Oct 18 14:15:15 2016 -0500 @@ -54,11 +54,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' backupdir = 'shelve-backup' shelvedir = 'shelved'
--- a/hgext/strip.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/strip.py Tue Oct 18 14:15:15 2016 -0500 @@ -23,11 +23,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' def checksubstate(repo, baserev=None): '''return list of subrepos at a different revision than substate.
--- a/hgext/transplant.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/transplant.py Tue Oct 18 14:15:15 2016 -0500 @@ -40,11 +40,11 @@ cmdtable = {} command = cmdutil.command(cmdtable) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' class transplantentry(object): def __init__(self, lnode, rnode):
--- a/hgext/win32mbcs.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/win32mbcs.py Tue Oct 18 14:15:15 2016 -0500 @@ -55,11 +55,11 @@ error, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' _encoding = None # see extsetup @@ -148,8 +148,8 @@ # NOTE: os.path.dirname() and os.path.basename() are safe because # they use result of os.path.split() funcs = '''os.path.join os.path.split os.path.splitext - os.path.normpath os.makedirs - mercurial.util.endswithsep mercurial.util.splitpath mercurial.util.checkcase + os.path.normpath os.makedirs mercurial.util.endswithsep + mercurial.util.splitpath mercurial.util.fscasesensitive mercurial.util.fspath mercurial.util.pconvert mercurial.util.normpath mercurial.util.checkwinfilename mercurial.util.checkosfilename mercurial.util.split'''
--- a/hgext/win32text.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/win32text.py Tue Oct 18 14:15:15 2016 -0500 @@ -52,11 +52,11 @@ util, ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # regexp for single LF without CR preceding. re_single_lf = re.compile('(^|[^\r])\n', re.MULTILINE)
--- a/hgext/zeroconf/__init__.py Tue Oct 18 14:13:06 2016 -0500 +++ b/hgext/zeroconf/__init__.py Tue Oct 18 14:15:15 2016 -0500 @@ -40,11 +40,11 @@ server as servermod ) -# Note for extension authors: ONLY specify testedwith = 'internal' for +# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' 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' +testedwith = 'ships-with-hg-core' # publish
--- a/i18n/hggettext Tue Oct 18 14:13:06 2016 -0500 +++ b/i18n/hggettext Tue Oct 18 14:15:15 2016 -0500 @@ -114,7 +114,7 @@ if func.__doc__: src = inspect.getsource(func) name = "%s.%s" % (path, func.__name__) - lineno = func.func_code.co_firstlineno + lineno = inspect.getsourcelines(func)[1] doc = func.__doc__ if rstrip: doc = doc.rstrip()
--- a/mercurial/__init__.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/__init__.py Tue Oct 18 14:15:15 2016 -0500 @@ -170,7 +170,7 @@ spec.loader = hgloader(spec.name, spec.origin) return spec - def replacetokens(tokens): + def replacetokens(tokens, fullname): """Transform a stream of tokens from raw to Python 3. It is called by the custom module loading machinery to rewrite @@ -184,6 +184,57 @@ REMEMBER TO CHANGE ``BYTECODEHEADER`` WHEN CHANGING THIS FUNCTION OR CACHED FILES WON'T GET INVALIDATED PROPERLY. """ + futureimpline = False + + # The following utility functions access the tokens list and i index of + # the for i, t enumerate(tokens) loop below + def _isop(j, *o): + """Assert that tokens[j] is an OP with one of the given values""" + try: + return tokens[j].type == token.OP and tokens[j].string in o + except IndexError: + return False + + def _findargnofcall(n): + """Find arg n of a call expression (start at 0) + + Returns index of the first token of that argument, or None if + there is not that many arguments. + + Assumes that token[i + 1] is '('. + + """ + nested = 0 + for j in range(i + 2, len(tokens)): + if _isop(j, ')', ']', '}'): + # end of call, tuple, subscription or dict / set + nested -= 1 + if nested < 0: + return None + elif n == 0: + # this is the starting position of arg + return j + elif _isop(j, '(', '[', '{'): + nested += 1 + elif _isop(j, ',') and nested == 0: + n -= 1 + + return None + + def _ensureunicode(j): + """Make sure the token at j is a unicode string + + This rewrites a string token to include the unicode literal prefix + so the string transformer won't add the byte prefix. + + Ignores tokens that are not strings. Assumes bounds checking has + already been done. + + """ + st = tokens[j] + if st.type == token.STRING and st.string.startswith(("'", '"')): + tokens[j] = st._replace(string='u%s' % st.string) + for i, t in enumerate(tokens): # Convert most string literals to byte literals. String literals # in Python 2 are bytes. String literals in Python 3 are unicode. @@ -213,64 +264,61 @@ continue # String literal. Prefix to make a b'' string. - yield tokenize.TokenInfo(t.type, 'b%s' % s, t.start, t.end, - t.line) + yield t._replace(string='b%s' % t.string) continue - try: - nexttoken = tokens[i + 1] - except IndexError: - nexttoken = None - - try: - prevtoken = tokens[i - 1] - except IndexError: - prevtoken = None + # Insert compatibility imports at "from __future__ import" line. + # No '\n' should be added to preserve line numbers. + if (t.type == token.NAME and t.string == 'import' and + all(u.type == token.NAME for u in tokens[i - 2:i]) and + [u.string for u in tokens[i - 2:i]] == ['from', '__future__']): + futureimpline = True + if t.type == token.NEWLINE and futureimpline: + futureimpline = False + if fullname == 'mercurial.pycompat': + yield t + continue + r, c = t.start + l = (b'; from mercurial.pycompat import ' + b'delattr, getattr, hasattr, setattr, xrange\n') + for u in tokenize.tokenize(io.BytesIO(l).readline): + if u.type in (tokenize.ENCODING, token.ENDMARKER): + continue + yield u._replace( + start=(r, c + u.start[1]), end=(r, c + u.end[1])) + continue # This looks like a function call. - if (t.type == token.NAME and nexttoken and - nexttoken.type == token.OP and nexttoken.string == '('): + if t.type == token.NAME and _isop(i + 1, '('): fn = t.string # *attr() builtins don't accept byte strings to 2nd argument. - # Rewrite the token to include the unicode literal prefix so - # the string transformer above doesn't add the byte prefix. - if fn in ('getattr', 'setattr', 'hasattr', 'safehasattr'): - try: - # (NAME, 'getattr') - # (OP, '(') - # (NAME, 'foo') - # (OP, ',') - # (NAME|STRING, foo) - st = tokens[i + 4] - if (st.type == token.STRING and - st.string[0] in ("'", '"')): - rt = tokenize.TokenInfo(st.type, 'u%s' % st.string, - st.start, st.end, st.line) - tokens[i + 4] = rt - except IndexError: - pass + if (fn in ('getattr', 'setattr', 'hasattr', 'safehasattr') and + not _isop(i - 1, '.')): + arg1idx = _findargnofcall(1) + if arg1idx is not None: + _ensureunicode(arg1idx) # .encode() and .decode() on str/bytes/unicode don't accept - # byte strings on Python 3. Rewrite the token to include the - # unicode literal prefix so the string transformer above doesn't - # add the byte prefix. - if (fn in ('encode', 'decode') and - prevtoken.type == token.OP and prevtoken.string == '.'): - # (OP, '.') - # (NAME, 'encode') - # (OP, '(') - # (STRING, 'utf-8') - # (OP, ')') - try: - st = tokens[i + 2] - if (st.type == token.STRING and - st.string[0] in ("'", '"')): - rt = tokenize.TokenInfo(st.type, 'u%s' % st.string, - st.start, st.end, st.line) - tokens[i + 2] = rt - except IndexError: - pass + # byte strings on Python 3. + elif fn in ('encode', 'decode') and _isop(i - 1, '.'): + for argn in range(2): + argidx = _findargnofcall(argn) + if argidx is not None: + _ensureunicode(argidx) + + # Bare open call (not an attribute on something else), the + # second argument (mode) must be a string, not bytes + elif fn == 'open' and not _isop(i - 1, '.'): + arg1idx = _findargnofcall(1) + if arg1idx is not None: + _ensureunicode(arg1idx) + + # It changes iteritems to items as iteritems is not + # present in Python 3 world. + elif fn == 'iteritems': + yield t._replace(string='items') + continue # Emit unmodified token. yield t @@ -279,7 +327,7 @@ # ``replacetoken`` or any mechanism that changes semantics of module # loading is changed. Otherwise cached bytecode may get loaded without # the new transformation mechanisms applied. - BYTECODEHEADER = b'HG\x00\x01' + BYTECODEHEADER = b'HG\x00\x06' class hgloader(importlib.machinery.SourceFileLoader): """Custom module loader that transforms source code. @@ -338,7 +386,7 @@ """Perform token transformation before compilation.""" buf = io.BytesIO(data) tokens = tokenize.tokenize(buf.readline) - data = tokenize.untokenize(replacetokens(list(tokens))) + data = tokenize.untokenize(replacetokens(list(tokens), self.name)) # Python's built-in importer strips frames from exceptions raised # for this code. Unfortunately, that mechanism isn't extensible # and our frame will be blamed for the import failure. There
--- a/mercurial/archival.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/archival.py Tue Oct 18 14:15:15 2016 -0500 @@ -231,7 +231,7 @@ if islink: mode = 0o777 ftype = _UNX_IFLNK - i.external_attr = (mode | ftype) << 16L + i.external_attr = (mode | ftype) << 16 # add "extended-timestamp" extra block, because zip archives # without this will be extracted with unexpected timestamp, # if TZ is not configured as GMT
--- a/mercurial/bdiff_module.c Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/bdiff_module.c Tue Oct 18 14:15:15 2016 -0500 @@ -17,6 +17,7 @@ #include "bdiff.h" #include "bitmanipulation.h" +#include "util.h" static PyObject *blocks(PyObject *self, PyObject *args)
--- a/mercurial/branchmap.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/branchmap.py Tue Oct 18 14:15:15 2016 -0500 @@ -470,8 +470,12 @@ def write(self, tr=None): """Save branch cache if it is dirty.""" repo = self._repo - if self._rbcnamescount < len(self._names): - try: + wlock = None + step = '' + try: + if self._rbcnamescount < len(self._names): + step = ' names' + wlock = repo.wlock(wait=False) if self._rbcnamescount != 0: f = repo.vfs.open(_rbcnames, 'ab') if f.tell() == self._rbcsnameslen: @@ -489,16 +493,15 @@ for b in self._names[self._rbcnamescount:])) self._rbcsnameslen = f.tell() f.close() - except (IOError, OSError, error.Abort) as inst: - repo.ui.debug("couldn't write revision branch cache names: " - "%s\n" % inst) - return - self._rbcnamescount = len(self._names) + self._rbcnamescount = len(self._names) - start = self._rbcrevslen * _rbcrecsize - if start != len(self._rbcrevs): - revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize) - try: + start = self._rbcrevslen * _rbcrecsize + if start != len(self._rbcrevs): + step = '' + if wlock is None: + wlock = repo.wlock(wait=False) + revs = min(len(repo.changelog), + len(self._rbcrevs) // _rbcrecsize) f = repo.vfs.open(_rbcrevs, 'ab') if f.tell() != start: repo.ui.debug("truncating %s to %s\n" % (_rbcrevs, start)) @@ -510,8 +513,10 @@ end = revs * _rbcrecsize f.write(self._rbcrevs[start:end]) f.close() - except (IOError, OSError, error.Abort) as inst: - repo.ui.debug("couldn't write revision branch cache: %s\n" % - inst) - return - self._rbcrevslen = revs + self._rbcrevslen = revs + except (IOError, OSError, error.Abort, error.LockError) as inst: + repo.ui.debug("couldn't write revision branch cache%s: %s\n" + % (step, inst)) + finally: + if wlock is not None: + wlock.release()
--- a/mercurial/bundle2.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/bundle2.py Tue Oct 18 14:15:15 2016 -0500 @@ -159,6 +159,7 @@ error, obsolete, pushkey, + pycompat, tags, url, util, @@ -572,7 +573,9 @@ yield param # starting compression for chunk in self._getcorechunk(): - yield self._compressor.compress(chunk) + data = self._compressor.compress(chunk) + if data: + yield data yield self._compressor.flush() def _paramchunk(self): @@ -996,7 +999,10 @@ outdebug(ui, 'closing payload chunk') # abort current part payload yield _pack(_fpayloadsize, 0) - raise exc_info[0], exc_info[1], exc_info[2] + if pycompat.ispy3: + raise exc_info[0](exc_info[1]).with_traceback(exc_info[2]) + else: + exec("""raise exc_info[0], exc_info[1], exc_info[2]""") # end of payload outdebug(ui, 'closing payload chunk') yield _pack(_fpayloadsize, 0) @@ -1320,7 +1326,9 @@ def chunkiter(): yield header for chunk in subchunkiter: - yield z.compress(chunk) + data = z.compress(chunk) + if data: + yield data yield z.flush() chunkiter = chunkiter()
--- a/mercurial/bundlerepo.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/bundlerepo.py Tue Oct 18 14:15:15 2016 -0500 @@ -56,10 +56,8 @@ self.repotiprev = n - 1 chain = None self.bundlerevs = set() # used by 'bundle()' revset expression - while True: - chunkdata = bundle.deltachunk(chain) - if not chunkdata: - break + getchunk = lambda: bundle.deltachunk(chain) + for chunkdata in iter(getchunk, {}): node = chunkdata['node'] p1 = chunkdata['p1'] p2 = chunkdata['p2'] @@ -190,22 +188,36 @@ self.filteredrevs = oldfilter class bundlemanifest(bundlerevlog, manifest.manifest): - def __init__(self, opener, bundle, linkmapper): - manifest.manifest.__init__(self, opener) + def __init__(self, opener, bundle, linkmapper, dirlogstarts=None, dir=''): + manifest.manifest.__init__(self, opener, dir=dir) bundlerevlog.__init__(self, opener, self.indexfile, bundle, linkmapper) + if dirlogstarts is None: + dirlogstarts = {} + if self.bundle.version == "03": + dirlogstarts = _getfilestarts(self.bundle) + self._dirlogstarts = dirlogstarts + self._linkmapper = linkmapper def baserevision(self, nodeorrev): node = nodeorrev if isinstance(node, int): node = self.node(node) - if node in self._mancache: - result = self._mancache[node][0].text() + if node in self.fulltextcache: + result = self.fulltextcache[node].tostring() else: result = manifest.manifest.revision(self, nodeorrev) return result + def dirlog(self, d): + if d in self._dirlogstarts: + self.bundle.seek(self._dirlogstarts[d]) + return bundlemanifest( + self.opener, self.bundle, self._linkmapper, + self._dirlogstarts, dir=d) + return super(bundlemanifest, self).dirlog(d) + class bundlefilelog(bundlerevlog, filelog.filelog): def __init__(self, opener, path, bundle, linkmapper): filelog.filelog.__init__(self, opener, path) @@ -236,6 +248,15 @@ self.invalidate() self.dirty = True +def _getfilestarts(bundle): + bundlefilespos = {} + for chunkdata in iter(bundle.filelogheader, {}): + fname = chunkdata['filename'] + bundlefilespos[fname] = bundle.tell() + for chunk in iter(lambda: bundle.deltachunk(None), {}): + pass + return bundlefilespos + class bundlerepository(localrepo.localrepository): def __init__(self, ui, path, bundlename): def _writetempbundle(read, suffix, header=''): @@ -283,7 +304,8 @@ "multiple changegroups") cgstream = part version = part.params.get('version', '01') - if version not in changegroup.allsupportedversions(ui): + legalcgvers = changegroup.supportedincomingversions(self) + if version not in legalcgvers: msg = _('Unsupported changegroup version: %s') raise error.Abort(msg % version) if self.bundle.compressed(): @@ -328,10 +350,6 @@ self.bundle.manifestheader() linkmapper = self.unfiltered().changelog.rev m = bundlemanifest(self.svfs, self.bundle, linkmapper) - # XXX: hack to work with changegroup3, but we still don't handle - # tree manifests correctly - if self.bundle.version == "03": - self.bundle.filelogheader() self.filestart = self.bundle.tell() return m @@ -351,16 +369,7 @@ def file(self, f): if not self.bundlefilespos: self.bundle.seek(self.filestart) - while True: - chunkdata = self.bundle.filelogheader() - if not chunkdata: - break - fname = chunkdata['filename'] - self.bundlefilespos[fname] = self.bundle.tell() - while True: - c = self.bundle.deltachunk(None) - if not c: - break + self.bundlefilespos = _getfilestarts(self.bundle) if f in self.bundlefilespos: self.bundle.seek(self.bundlefilespos[f]) @@ -480,7 +489,10 @@ if bundlename or not localrepo: # create a bundle (uncompressed if other repo is not local) - canbundle2 = (ui.configbool('experimental', 'bundle2-exp', True) + # developer config: devel.legacy.exchange + legexc = ui.configlist('devel', 'legacy.exchange') + forcebundle1 = 'bundle2' not in legexc and 'bundle1' in legexc + canbundle2 = (not forcebundle1 and other.capable('getbundle') and other.capable('bundle2')) if canbundle2:
--- a/mercurial/changegroup.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/changegroup.py Tue Oct 18 14:15:15 2016 -0500 @@ -15,7 +15,6 @@ from .i18n import _ from .node import ( hex, - nullid, nullrev, short, ) @@ -94,7 +93,9 @@ if vfs: fh = vfs.open(filename, "wb") else: - fh = open(filename, "wb") + # Increase default buffer size because default is usually + # small (4k is common on Linux). + fh = open(filename, "wb", 131072) else: fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg") fh = os.fdopen(fd, "wb") @@ -333,7 +334,7 @@ for cset in xrange(clstart, clend): mfnode = repo.changelog.read( repo.changelog.node(cset))[0] - mfest = repo.manifest.readdelta(mfnode) + mfest = repo.manifestlog[mfnode].readdelta() # store file nodes we must see for f, n in mfest.iteritems(): needfiles.setdefault(f, set()).add(n) @@ -404,6 +405,7 @@ # coming call to `destroyed` will repair it. # In other case we can safely update cache on # disk. + repo.ui.debug('updating the branch cache\n') branchmap.updatecache(repo.filtered('served')) def runhooks(): @@ -413,8 +415,6 @@ if clstart >= len(repo): return - # forcefully update the on-disk branch cache - repo.ui.debug("updating the branch cache\n") repo.hook("changegroup", **hookargs) for n in added: @@ -475,10 +475,7 @@ def _unpackmanifests(self, repo, revmap, trp, prog, numchanges): super(cg3unpacker, self)._unpackmanifests(repo, revmap, trp, prog, numchanges) - while True: - chunkdata = self.filelogheader() - if not chunkdata: - break + for chunkdata in iter(self.filelogheader, {}): # If we get here, there are directory manifests in the changegroup d = chunkdata["filename"] repo.ui.debug("adding %s revisions\n" % d) @@ -823,11 +820,24 @@ def deltaparent(self, revlog, rev, p1, p2, prev): dp = revlog.deltaparent(rev) - # avoid storing full revisions; pick prev in those cases - # also pick prev when we can't be sure remote has dp - if dp == nullrev or (dp != p1 and dp != p2 and dp != prev): + if dp == nullrev and revlog.storedeltachains: + # Avoid sending full revisions when delta parent is null. Pick prev + # in that case. It's tempting to pick p1 in this case, as p1 will + # be smaller in the common case. However, computing a delta against + # p1 may require resolving the raw text of p1, which could be + # expensive. The revlog caches should have prev cached, meaning + # less CPU for changegroup generation. There is likely room to add + # a flag and/or config option to control this behavior. return prev - return dp + elif dp == nullrev: + # revlog is configured to use full snapshot for a reason, + # stick to full snapshot. + return nullrev + elif dp not in (p1, p2, prev): + # Pick prev when we can't be sure remote has the base revision. + return prev + else: + return dp def builddeltaheader(self, node, p1n, p2n, basenode, linknode, flags): # Do nothing with flags, it is implicitly 0 in cg1 and cg2 @@ -946,17 +956,7 @@ Another wrinkle is doing the reverse, figuring out which changeset in the changegroup a particular filenode or manifestnode belongs to. """ - cl = repo.changelog - if not roots: - roots = [nullid] - discbases = [] - for n in roots: - discbases.extend([p for p in cl.parents(n) if p != nullid]) - # TODO: remove call to nodesbetween. - csets, roots, heads = cl.nodesbetween(roots, heads) - included = set(csets) - discbases = [n for n in discbases if n not in included] - outgoing = discovery.outgoing(cl, discbases, heads) + outgoing = discovery.outgoing(repo, missingroots=roots, missingheads=heads) bundler = getbundler(version, repo) return getsubset(repo, outgoing, bundler, source) @@ -982,26 +982,7 @@ bundler = getbundler(version, repo, bundlecaps) return getsubset(repo, outgoing, bundler, source) -def computeoutgoing(repo, heads, common): - """Computes which revs are outgoing given a set of common - and a set of heads. - - This is a separate function so extensions can have access to - the logic. - - Returns a discovery.outgoing object. - """ - cl = repo.changelog - if common: - hasnode = cl.hasnode - common = [n for n in common if hasnode(n)] - else: - common = [nullid] - if not heads: - heads = cl.heads() - return discovery.outgoing(cl, common, heads) - -def getchangegroup(repo, source, heads=None, common=None, bundlecaps=None, +def getchangegroup(repo, source, outgoing, bundlecaps=None, version='01'): """Like changegroupsubset, but returns the set difference between the ancestors of heads and the ancestors common. @@ -1011,7 +992,6 @@ The nodes in common might not all be known locally due to the way the current discovery protocol works. """ - outgoing = computeoutgoing(repo, heads, common) return getlocalchangegroup(repo, source, outgoing, bundlecaps=bundlecaps, version=version) @@ -1022,10 +1002,7 @@ def _addchangegroupfiles(repo, source, revmap, trp, expectedfiles, needfiles): revisions = 0 files = 0 - while True: - chunkdata = source.filelogheader() - if not chunkdata: - break + for chunkdata in iter(source.filelogheader, {}): files += 1 f = chunkdata["filename"] repo.ui.debug("adding %s revisions\n" % f)
--- a/mercurial/changelog.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/changelog.py Tue Oct 18 14:15:15 2016 -0500 @@ -124,7 +124,7 @@ def _divertopener(opener, target): """build an opener that writes in 'target.a' instead of 'target'""" - def _divert(name, mode='r'): + def _divert(name, mode='r', checkambig=False): if name != target: return opener(name, mode) return opener(name + ".a", mode) @@ -132,15 +132,16 @@ def _delayopener(opener, target, buf): """build an opener that stores chunks in 'buf' instead of 'target'""" - def _delay(name, mode='r'): + def _delay(name, mode='r', checkambig=False): if name != target: return opener(name, mode) return appender(opener, name, mode, buf) return _delay -_changelogrevision = collections.namedtuple('changelogrevision', - ('manifest', 'user', 'date', - 'files', 'description', 'extra')) +_changelogrevision = collections.namedtuple(u'changelogrevision', + (u'manifest', u'user', u'date', + u'files', u'description', + u'extra')) class changelogrevision(object): """Holds results of a parsed changelog revision. @@ -151,8 +152,8 @@ """ __slots__ = ( - '_offsets', - '_text', + u'_offsets', + u'_text', ) def __new__(cls, text): @@ -256,11 +257,18 @@ class changelog(revlog.revlog): def __init__(self, opener): - revlog.revlog.__init__(self, opener, "00changelog.i") + revlog.revlog.__init__(self, opener, "00changelog.i", + checkambig=True) if self._initempty: # changelogs don't benefit from generaldelta self.version &= ~revlog.REVLOGGENERALDELTA self._generaldelta = False + + # Delta chains for changelogs tend to be very small because entries + # tend to be small and don't delta well with each. So disable delta + # chains. + self.storedeltachains = False + self._realopener = opener self._delayed = False self._delaybuf = None @@ -381,9 +389,9 @@ tmpname = self.indexfile + ".a" nfile = self.opener.open(tmpname) nfile.close() - self.opener.rename(tmpname, self.indexfile) + self.opener.rename(tmpname, self.indexfile, checkambig=True) elif self._delaybuf: - fp = self.opener(self.indexfile, 'a') + fp = self.opener(self.indexfile, 'a', checkambig=True) fp.write("".join(self._delaybuf)) fp.close() self._delaybuf = None
--- a/mercurial/cmdutil.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/cmdutil.py Tue Oct 18 14:15:15 2016 -0500 @@ -499,6 +499,12 @@ def __getattr__(self, attr): return getattr(self._fp, attr) + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + pass + def makefileobj(repo, pat, node=None, desc=None, total=None, seqno=None, revwidth=None, mode='wb', modemap=None, pathname=None): @@ -549,7 +555,7 @@ if 'treemanifest' not in repo.requirements: raise error.Abort(_("--dir can only be used on repos with " "treemanifest enabled")) - dirlog = repo.dirlog(dir) + dirlog = repo.manifest.dirlog(dir) if len(dirlog): r = dirlog elif mf: @@ -640,8 +646,26 @@ if not after and exists or after and state in 'mn': if not opts['force']: - ui.warn(_('%s: not overwriting - file exists\n') % - reltarget) + if state in 'mn': + msg = _('%s: not overwriting - file already committed\n') + if after: + flags = '--after --force' + else: + flags = '--force' + if rename: + hint = _('(hg rename %s to replace the file by ' + 'recording a rename)\n') % flags + else: + hint = _('(hg copy %s to replace the file by ' + 'recording a copy)\n') % flags + else: + msg = _('%s: not overwriting - file exists\n') + if rename: + hint = _('(hg rename --after to record the rename)\n') + else: + hint = _('(hg copy --after to record the copy)\n') + ui.warn(msg % reltarget) + ui.warn(hint) return if after: @@ -1611,25 +1635,26 @@ return changeset_templater(ui, repo, matchfn, opts, tmpl, mapfile, buffered) -def showmarker(ui, marker, index=None): +def showmarker(fm, marker, index=None): """utility function to display obsolescence marker in a readable way To be used by debug function.""" if index is not None: - ui.write("%i " % index) - ui.write(hex(marker.precnode())) - for repl in marker.succnodes(): - ui.write(' ') - ui.write(hex(repl)) - ui.write(' %X ' % marker.flags()) + fm.write('index', '%i ', index) + fm.write('precnode', '%s ', hex(marker.precnode())) + succs = marker.succnodes() + fm.condwrite(succs, 'succnodes', '%s ', + fm.formatlist(map(hex, succs), name='node')) + fm.write('flag', '%X ', marker.flags()) parents = marker.parentnodes() if parents is not None: - ui.write('{%s} ' % ', '.join(hex(p) for p in parents)) - ui.write('(%s) ' % util.datestr(marker.date())) - ui.write('{%s}' % (', '.join('%r: %r' % t for t in - sorted(marker.metadata().items()) - if t[0] != 'date'))) - ui.write('\n') + fm.write('parentnodes', '{%s} ', + fm.formatlist(map(hex, parents), name='node', sep=', ')) + fm.write('date', '(%s) ', fm.formatdate(marker.date())) + meta = marker.metadata().copy() + meta.pop('date', None) + fm.write('metadata', '{%s}', fm.formatdict(meta, fmt='%r: %r', sep=', ')) + fm.plain('\n') def finddate(ui, repo, date): """Find the tipmost changeset that matches the given date spec""" @@ -1940,7 +1965,7 @@ # --follow, we want the names of the ancestors of FILE in the # revision, stored in "fcache". "fcache" is populated by # reproducing the graph traversal already done by --follow revset - # and relating linkrevs to file names (which is not "correct" but + # and relating revs to file names (which is not "correct" but # good enough). fcache = {} fcacheready = [False] @@ -1948,9 +1973,10 @@ def populate(): for fn in files: - for i in ((pctx[fn],), pctx[fn].ancestors(followfirst=followfirst)): - for c in i: - fcache.setdefault(c.linkrev(), set()).add(c.path()) + fctx = pctx[fn] + fcache.setdefault(fctx.introrev(), set()).add(fctx.path()) + for c in fctx.ancestors(followfirst=followfirst): + fcache.setdefault(c.rev(), set()).add(c.path()) def filematcher(rev): if not fcacheready[0]: @@ -2151,15 +2177,8 @@ 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. - revs.reverse() - matcher = revset.match(repo.ui, expr) - # Revset matches can reorder revisions. "A or B" typically returns - # returns the revision matching A then the revision matching B. Sort - # again to fix that. + matcher = revset.match(repo.ui, expr, order=revset.followorder) revs = matcher(repo, revs) - revs.sort(reverse=True) if limit is not None: limitedrevs = [] for idx, rev in enumerate(revs): @@ -2184,23 +2203,8 @@ return revset.baseset([]), None, None expr, filematcher = _makelogrevset(repo, pats, opts, revs) if expr: - # Revset matchers often operate faster on revisions in changelog - # order, because most filters deal with the changelog. - if not opts.get('rev'): - revs.reverse() - matcher = revset.match(repo.ui, expr) - # Revset matches can reorder revisions. "A or B" typically returns - # returns the revision matching A then the revision matching B. Sort - # again to fix that. - fixopts = ['branch', 'only_branch', 'keyword', 'user'] - oldrevs = revs + matcher = revset.match(repo.ui, expr, order=revset.followorder) revs = matcher(repo, revs) - if not opts.get('rev'): - revs.sort(reverse=True) - elif len(pats) > 1 or any(len(opts.get(op, [])) > 1 for op in fixopts): - # XXX "A or B" is known to change the order; fix it by filtering - # matched set again (issue5100) - revs = oldrevs & revs if limit is not None: limitedrevs = [] for idx, r in enumerate(revs): @@ -2415,14 +2419,10 @@ ret = 0 for subpath in sorted(ctx.substate): - def matchessubrepo(subpath): - return (m.exact(subpath) - or any(f.startswith(subpath + '/') for f in m.files())) - - if subrepos or matchessubrepo(subpath): + submatch = matchmod.subdirmatcher(subpath, m) + if (subrepos or m.exact(subpath) or any(submatch.files())): sub = ctx.sub(subpath) try: - submatch = matchmod.subdirmatcher(subpath, m) recurse = m.exact(subpath) or subrepos if sub.printfiles(ui, submatch, fm, fmt, recurse) == 0: ret = 0 @@ -2450,21 +2450,12 @@ total = len(subs) count = 0 for subpath in subs: - def matchessubrepo(matcher, subpath): - if matcher.exact(subpath): - return True - for f in matcher.files(): - if f.startswith(subpath): - return True - return False - count += 1 - if subrepos or matchessubrepo(m, subpath): + submatch = matchmod.subdirmatcher(subpath, m) + if subrepos or m.exact(subpath) or any(submatch.files()): ui.progress(_('searching'), count, total=total, unit=_('subrepos')) - sub = wctx.sub(subpath) try: - submatch = matchmod.subdirmatcher(subpath, m) if sub.removefiles(submatch, prefix, after, force, subrepos, warnings): ret = 1 @@ -2530,8 +2521,8 @@ for f in added: count += 1 ui.progress(_('skipping'), count, total=total, unit=_('files')) - warnings.append(_('not removing %s: file has been marked for add' - ' (use forget to undo)\n') % m.rel(f)) + warnings.append(_("not removing %s: file has been marked for add" + " (use 'hg forget' to undo add)\n") % m.rel(f)) ret = 1 ui.progress(_('skipping'), None) @@ -2581,14 +2572,7 @@ write(file) return 0 - # Don't warn about "missing" files that are really in subrepos - def badfn(path, msg): - for subpath in ctx.substate: - if path.startswith(subpath + '/'): - return - matcher.bad(path, msg) - - for abs in ctx.walk(matchmod.badmatch(matcher, badfn)): + for abs in ctx.walk(matcher): write(abs) err = 0 @@ -2623,6 +2607,18 @@ return commitfunc(ui, repo, message, matcher, opts) +def samefile(f, ctx1, ctx2): + if f in ctx1.manifest(): + a = ctx1.filectx(f) + if f in ctx2.manifest(): + b = ctx2.filectx(f) + return (not a.cmp(b) + and a.flags() == b.flags()) + else: + return False + else: + return f not in ctx2.manifest() + def amend(ui, repo, commitfunc, old, extra, pats, opts): # avoid cycle context -> subrepo -> cmdutil from . import context @@ -2706,19 +2702,7 @@ # we can discard X from our list of files. Likewise if X # was deleted, it's no longer relevant files.update(ctx.files()) - - def samefile(f): - if f in ctx.manifest(): - a = ctx.filectx(f) - if f in base.manifest(): - b = base.filectx(f) - return (not a.cmp(b) - and a.flags() == b.flags()) - else: - return False - else: - return f not in base.manifest() - files = [f for f in files if not samefile(f)] + files = [f for f in files if not samefile(f, ctx, base)] def filectxfn(repo, ctx_, path): try: @@ -3542,10 +3526,11 @@ def __init__(self, repo, name): self._repo = repo + self._active = False + self._closed = False self._suffix = '.backup.%s.%d' % (name, id(self)) repo.dirstate.savebackup(repo.currenttransaction(), self._suffix) self._active = True - self._closed = False def __del__(self): if self._active: # still active
--- a/mercurial/commands.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/commands.py Tue Oct 18 14:15:15 2016 -0500 @@ -441,12 +441,12 @@ if linenumber and (not opts.get('changeset')) and (not opts.get('number')): raise error.Abort(_('at least one of -n/-c is required for -l')) - if fm: + if fm.isplain(): + def makefunc(get, fmt): + return lambda x: fmt(get(x)) + else: def makefunc(get, fmt): return get - else: - def makefunc(get, fmt): - return lambda x: fmt(get(x)) funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap if opts.get(op)] funcmap[0] = (funcmap[0][0], '') # no separator in front of first column @@ -476,12 +476,12 @@ for f, sep in funcmap: l = [f(n) for n, dummy in lines] - if fm: - formats.append(['%s' for x in l]) - else: + if fm.isplain(): sizes = [encoding.colwidth(x) for x in l] ml = max(sizes) formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes]) + else: + formats.append(['%s' for x in l]) pieces.append(l) for f, p, l in zip(zip(*formats), zip(*pieces), lines): @@ -835,57 +835,6 @@ Returns 0 on success. """ - def extendbisectrange(nodes, good): - # bisect is incomplete when it ends on a merge node and - # one of the parent was not checked. - parents = repo[nodes[0]].parents() - if len(parents) > 1: - if good: - side = state['bad'] - else: - side = state['good'] - num = len(set(i.node() for i in parents) & set(side)) - if num == 1: - return parents[0].ancestor(parents[1]) - return None - - def print_result(nodes, good): - displayer = cmdutil.show_changeset(ui, repo, {}) - if len(nodes) == 1: - # narrowed it down to a single revision - if good: - ui.write(_("The first good revision is:\n")) - else: - ui.write(_("The first bad revision is:\n")) - displayer.show(repo[nodes[0]]) - extendnode = extendbisectrange(nodes, good) - if extendnode is not None: - ui.write(_('Not all ancestors of this changeset have been' - ' checked.\nUse bisect --extend to continue the ' - 'bisection from\nthe common ancestor, %s.\n') - % extendnode) - else: - # multiple possible revisions - if good: - ui.write(_("Due to skipped revisions, the first " - "good revision could be any of:\n")) - else: - ui.write(_("Due to skipped revisions, the first " - "bad revision could be any of:\n")) - for n in nodes: - displayer.show(repo[n]) - displayer.close() - - def check_state(state, interactive=True): - if not state['good'] or not state['bad']: - if (good or bad or skip or reset) and interactive: - return - if not state['good']: - raise error.Abort(_('cannot bisect (no known good revisions)')) - else: - raise error.Abort(_('cannot bisect (no known bad revisions)')) - return True - # backward compatibility if rev in "good bad reset init".split(): ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n")) @@ -902,13 +851,36 @@ cmdutil.checkunfinished(repo) if reset: - p = repo.join("bisect.state") - if os.path.exists(p): - os.unlink(p) + hbisect.resetstate(repo) return state = hbisect.load_state(repo) + # update state + if good or bad or skip: + if rev: + nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])] + else: + nodes = [repo.lookup('.')] + if good: + state['good'] += nodes + elif bad: + state['bad'] += nodes + elif skip: + state['skip'] += nodes + hbisect.save_state(repo, state) + if not (state['good'] and state['bad']): + return + + def mayupdate(repo, node, show_stats=True): + """common used update sequence""" + if noupdate: + return + cmdutil.bailifchanged(repo) + return hg.clean(repo, node, show_stats=show_stats) + + displayer = cmdutil.show_changeset(ui, repo, {}) + if command: changesets = 1 if noupdate: @@ -921,6 +893,8 @@ node, p2 = repo.dirstate.parents() if p2 != nullid: raise error.Abort(_('current bisect revision is a merge')) + if rev: + node = repo[scmutil.revsingle(repo, rev, node)].node() try: while changesets: # update state @@ -938,61 +912,38 @@ raise error.Abort(_("%s killed") % command) else: transition = "bad" - ctx = scmutil.revsingle(repo, rev, node) - rev = None # clear for future iterations - state[transition].append(ctx.node()) + state[transition].append(node) + ctx = repo[node] ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition)) - check_state(state, interactive=False) + hbisect.checkstate(state) # bisect nodes, changesets, bgood = hbisect.bisect(repo.changelog, state) # update to next check node = nodes[0] - if not noupdate: - cmdutil.bailifchanged(repo) - hg.clean(repo, node, show_stats=False) + mayupdate(repo, node, show_stats=False) finally: state['current'] = [node] hbisect.save_state(repo, state) - print_result(nodes, bgood) + hbisect.printresult(ui, repo, state, displayer, nodes, bgood) return - # update state - - if rev: - nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])] - else: - nodes = [repo.lookup('.')] - - if good or bad or skip: - if good: - state['good'] += nodes - elif bad: - state['bad'] += nodes - elif skip: - state['skip'] += nodes - hbisect.save_state(repo, state) - - if not check_state(state): - return + hbisect.checkstate(state) # actually bisect nodes, changesets, good = hbisect.bisect(repo.changelog, state) if extend: if not changesets: - extendnode = extendbisectrange(nodes, good) + extendnode = hbisect.extendrange(repo, state, nodes, good) if extendnode is not None: ui.write(_("Extending search to changeset %d:%s\n") % (extendnode.rev(), extendnode)) state['current'] = [extendnode.node()] hbisect.save_state(repo, state) - if noupdate: - return - cmdutil.bailifchanged(repo) - return hg.clean(repo, extendnode.node()) + return mayupdate(repo, extendnode.node()) raise error.Abort(_("nothing to extend")) if changesets == 0: - print_result(nodes, good) + hbisect.printresult(ui, repo, state, displayer, nodes, good) else: assert len(nodes) == 1 # only a single node can be tested next node = nodes[0] @@ -1006,9 +957,7 @@ % (rev, short(node), changesets, tests)) state['current'] = [node] hbisect.save_state(repo, state) - if not noupdate: - cmdutil.bailifchanged(repo) - return hg.clean(repo, node) + return mayupdate(repo, node) @command('bookmarks|bookmark', [('f', 'force', False, _('force')), @@ -1185,7 +1134,7 @@ fm = ui.formatter('bookmarks', opts) hexfn = fm.hexfunc marks = repo._bookmarks - if len(marks) == 0 and not fm: + if len(marks) == 0 and fm.isplain(): ui.status(_("no bookmarks set\n")) for bmark, n in sorted(marks.iteritems()): active = repo._activebookmark @@ -1383,8 +1332,8 @@ repo, bundletype, strict=False) except error.UnsupportedBundleSpecification as e: raise error.Abort(str(e), - hint=_('see "hg help bundle" for supported ' - 'values for --type')) + hint=_("see 'hg help bundle' for supported " + "values for --type")) # Packed bundles are a pseudo bundle format for now. if cgversion == 's1': @@ -1411,10 +1360,11 @@ raise error.Abort(_("--base is incompatible with specifying " "a destination")) common = [repo.lookup(rev) for rev in base] - heads = revs and map(repo.lookup, revs) or revs - cg = changegroup.getchangegroup(repo, 'bundle', heads=heads, - common=common, bundlecaps=bundlecaps, - version=cgversion) + heads = revs and map(repo.lookup, revs) or None + outgoing = discovery.outgoing(repo, common, heads) + cg = changegroup.getchangegroup(repo, 'bundle', outgoing, + bundlecaps=bundlecaps, + version=cgversion) outgoing = None else: dest = ui.expandpath(dest or 'default-push', dest or 'default') @@ -1688,9 +1638,11 @@ def _docommit(ui, repo, *pats, **opts): if opts.get('interactive'): opts.pop('interactive') - cmdutil.dorecord(ui, repo, commit, None, False, - cmdutil.recordfilter, *pats, **opts) - return + ret = cmdutil.dorecord(ui, repo, commit, None, False, + cmdutil.recordfilter, *pats, **opts) + # ret can be 0 (no changes to record) or the value returned by + # commit(), 1 if nothing changed or None on success. + return 1 if ret == 0 else ret if opts.get('subrepos'): if opts.get('amend'): @@ -1787,7 +1739,7 @@ [('u', 'untrusted', None, _('show untrusted configuration options')), ('e', 'edit', None, _('edit user config')), ('l', 'local', None, _('edit repository config')), - ('g', 'global', None, _('edit global config'))], + ('g', 'global', None, _('edit global config'))] + formatteropts, _('[-u] [NAME]...'), optionalrepo=True) def config(ui, repo, *values, **opts): @@ -1848,6 +1800,7 @@ onerr=error.Abort, errprefix=_("edit failed")) return + fm = ui.formatter('config', opts) for f in scmutil.rcpath(): ui.debug('read config from: %s\n' % f) untrusted = bool(opts.get('untrusted')) @@ -1858,25 +1811,32 @@ raise error.Abort(_('only one config item permitted')) matched = False for section, name, value in ui.walkconfig(untrusted=untrusted): - value = str(value).replace('\n', '\\n') - sectname = section + '.' + name + value = str(value) + if fm.isplain(): + value = value.replace('\n', '\\n') + entryname = section + '.' + name if values: for v in values: if v == section: - ui.debug('%s: ' % - ui.configsource(section, name, untrusted)) - ui.write('%s=%s\n' % (sectname, value)) + fm.startitem() + fm.condwrite(ui.debugflag, 'source', '%s: ', + ui.configsource(section, name, untrusted)) + fm.write('name value', '%s=%s\n', entryname, value) matched = True - elif v == sectname: - ui.debug('%s: ' % - ui.configsource(section, name, untrusted)) - ui.write(value, '\n') + elif v == entryname: + fm.startitem() + fm.condwrite(ui.debugflag, 'source', '%s: ', + ui.configsource(section, name, untrusted)) + fm.write('value', '%s\n', value) + fm.data(name=entryname) matched = True else: - ui.debug('%s: ' % - ui.configsource(section, name, untrusted)) - ui.write('%s=%s\n' % (sectname, value)) + fm.startitem() + fm.condwrite(ui.debugflag, 'source', '%s: ', + ui.configsource(section, name, untrusted)) + fm.write('name value', '%s=%s\n', entryname, value) matched = True + fm.end() if matched: return 0 return 1 @@ -1987,8 +1947,9 @@ tags = [] - lock = tr = None + wlock = lock = tr = None try: + wlock = repo.wlock() lock = repo.lock() tr = repo.transaction("builddag") @@ -2073,7 +2034,7 @@ repo.vfs.write("localtags", "".join(tags)) finally: ui.progress(_('building'), None) - release(tr, lock) + release(tr, lock, wlock) @command('debugbundle', [('a', 'all', None, _('show all details')), @@ -2102,10 +2063,7 @@ def showchunks(named): ui.write("\n%s%s\n" % (indent_string, named)) chain = None - while True: - chunkdata = gen.deltachunk(chain) - if not chunkdata: - break + for chunkdata in iter(lambda: gen.deltachunk(chain), {}): node = chunkdata['node'] p1 = chunkdata['p1'] p2 = chunkdata['p2'] @@ -2121,10 +2079,7 @@ showchunks("changelog") chunkdata = gen.manifestheader() showchunks("manifest") - while True: - chunkdata = gen.filelogheader() - if not chunkdata: - break + for chunkdata in iter(gen.filelogheader, {}): fname = chunkdata['filename'] showchunks(fname) else: @@ -2132,10 +2087,7 @@ raise error.Abort(_('use debugbundle2 for this file')) chunkdata = gen.changelogheader() chain = None - while True: - chunkdata = gen.deltachunk(chain) - if not chunkdata: - break + for chunkdata in iter(lambda: gen.deltachunk(chain), {}): node = chunkdata['node'] ui.write("%s%s\n" % (indent_string, hex(node))) chain = node @@ -2398,12 +2350,15 @@ def debugextensions(ui, **opts): '''show information about active extensions''' exts = extensions.extensions(ui) + hgver = util.version() fm = ui.formatter('debugextensions', opts) for extname, extmod in sorted(exts, key=operator.itemgetter(0)): + isinternal = extensions.ismoduleinternal(extmod) extsource = extmod.__file__ - exttestedwith = getattr(extmod, 'testedwith', None) - if exttestedwith is not None: - exttestedwith = exttestedwith.split() + if isinternal: + exttestedwith = [] # never expose magic string to users + else: + exttestedwith = getattr(extmod, 'testedwith', '').split() extbuglink = getattr(extmod, 'buglink', None) fm.startitem() @@ -2412,21 +2367,24 @@ fm.write('name', '%s\n', extname) else: fm.write('name', '%s', extname) - if not exttestedwith: + if isinternal or hgver in exttestedwith: + fm.plain('\n') + elif not exttestedwith: fm.plain(_(' (untested!)\n')) else: - if exttestedwith == ['internal'] or \ - util.version() in exttestedwith: - fm.plain('\n') - else: - lasttestedversion = exttestedwith[-1] - fm.plain(' (%s!)\n' % lasttestedversion) + lasttestedversion = exttestedwith[-1] + fm.plain(' (%s!)\n' % lasttestedversion) fm.condwrite(ui.verbose and extsource, 'source', _(' location: %s\n'), extsource or "") + if ui.verbose: + fm.plain(_(' bundled: %s\n') % ['no', 'yes'][isinternal]) + fm.data(bundled=isinternal) + fm.condwrite(ui.verbose and exttestedwith, 'testedwith', - _(' tested with: %s\n'), ' '.join(exttestedwith or [])) + _(' tested with: %s\n'), + fm.formatlist(exttestedwith, name='ver')) fm.condwrite(ui.verbose and extbuglink, 'buglink', _(' bug reporting: %s\n'), extbuglink or "") @@ -2453,7 +2411,7 @@ ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no')) ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no')) ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no')) - ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo') + ui.write(('case-sensitive: %s\n') % (util.fscasesensitive('.debugfsinfo') and 'yes' or 'no')) os.unlink('.debugfsinfo') @@ -2832,7 +2790,7 @@ if not problems: fm.data(problems=problems) fm.condwrite(problems, 'problems', - _("%s problems detected," + _("%d problems detected," " please check your install!\n"), problems) fm.end() @@ -3054,7 +3012,7 @@ ('r', 'rev', [], _('display markers relevant to REV')), ('', 'index', False, _('display index of the marker')), ('', 'delete', [], _('delete markers specified by indices')), - ] + commitopts2, + ] + commitopts2 + formatteropts, _('[OBSOLETED [REPLACEMENT ...]]')) def debugobsolete(ui, repo, precursor=None, *successors, **opts): """create arbitrary obsolete marker @@ -3142,6 +3100,7 @@ markerset = set(markers) isrelevant = lambda m: m in markerset + fm = ui.formatter('debugobsolete', opts) for i, m in enumerate(markerstoiter): if not isrelevant(m): # marker can be irrelevant when we're iterating over a set @@ -3152,8 +3111,10 @@ # to get the correct indices, but only display the ones that # are relevant to --rev value continue + fm.startitem() ind = i if opts.get('index') else None - cmdutil.showmarker(ui, m, index=ind) + cmdutil.showmarker(fm, m, index=ind) + fm.end() @command('debugpathcomplete', [('f', 'full', None, _('complete an entire path')), @@ -3382,9 +3343,9 @@ nump2prev = 0 chainlengths = [] - datasize = [None, 0, 0L] - fullsize = [None, 0, 0L] - deltasize = [None, 0, 0L] + datasize = [None, 0, 0] + fullsize = [None, 0, 0] + deltasize = [None, 0, 0] def addsize(size, l): if l[0] is None or size < l[0]: @@ -3509,29 +3470,92 @@ numdeltas)) @command('debugrevspec', - [('', 'optimize', None, _('print parsed tree after optimizing'))], + [('', 'optimize', None, + _('print parsed tree after optimizing (DEPRECATED)')), + ('p', 'show-stage', [], + _('print parsed tree at the given stage'), _('NAME')), + ('', 'no-optimized', False, _('evaluate tree without optimization')), + ('', 'verify-optimized', False, _('verify optimized result')), + ], ('REVSPEC')) def debugrevspec(ui, repo, expr, **opts): """parse and apply a revision specification - Use --verbose to print the parsed tree before and after aliases - expansion. + Use -p/--show-stage option to print the parsed tree at the given stages. + Use -p all to print tree at every stage. + + Use --verify-optimized to compare the optimized result with the unoptimized + one. Returns 1 if the optimized result differs. """ - if ui.verbose: - tree = revset.parse(expr, lookup=repo.__contains__) - ui.note(revset.prettyformat(tree), "\n") - newtree = revset.expandaliases(ui, tree) - if newtree != tree: - 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") - if opts["optimize"]: - optimizedtree = revset.optimize(newtree) - ui.note(("* optimized:\n"), - revset.prettyformat(optimizedtree), "\n") - func = revset.match(ui, expr, repo) + stages = [ + ('parsed', lambda tree: tree), + ('expanded', lambda tree: revset.expandaliases(ui, tree)), + ('concatenated', revset.foldconcat), + ('analyzed', revset.analyze), + ('optimized', revset.optimize), + ] + if opts['no_optimized']: + stages = stages[:-1] + if opts['verify_optimized'] and opts['no_optimized']: + raise error.Abort(_('cannot use --verify-optimized with ' + '--no-optimized')) + stagenames = set(n for n, f in stages) + + showalways = set() + showchanged = set() + if ui.verbose and not opts['show_stage']: + # show parsed tree by --verbose (deprecated) + showalways.add('parsed') + showchanged.update(['expanded', 'concatenated']) + if opts['optimize']: + showalways.add('optimized') + if opts['show_stage'] and opts['optimize']: + raise error.Abort(_('cannot use --optimize with --show-stage')) + if opts['show_stage'] == ['all']: + showalways.update(stagenames) + else: + for n in opts['show_stage']: + if n not in stagenames: + raise error.Abort(_('invalid stage name: %s') % n) + showalways.update(opts['show_stage']) + + treebystage = {} + printedtree = None + tree = revset.parse(expr, lookup=repo.__contains__) + for n, f in stages: + treebystage[n] = tree = f(tree) + if n in showalways or (n in showchanged and tree != printedtree): + if opts['show_stage'] or n != 'parsed': + ui.write(("* %s:\n") % n) + ui.write(revset.prettyformat(tree), "\n") + printedtree = tree + + if opts['verify_optimized']: + arevs = revset.makematcher(treebystage['analyzed'])(repo) + brevs = revset.makematcher(treebystage['optimized'])(repo) + if ui.verbose: + ui.note(("* analyzed set:\n"), revset.prettyformatset(arevs), "\n") + ui.note(("* optimized set:\n"), revset.prettyformatset(brevs), "\n") + arevs = list(arevs) + brevs = list(brevs) + if arevs == brevs: + return 0 + ui.write(('--- analyzed\n'), label='diff.file_a') + ui.write(('+++ optimized\n'), label='diff.file_b') + sm = difflib.SequenceMatcher(None, arevs, brevs) + for tag, alo, ahi, blo, bhi in sm.get_opcodes(): + if tag in ('delete', 'replace'): + for c in arevs[alo:ahi]: + ui.write('-%s\n' % c, label='diff.deleted') + if tag in ('insert', 'replace'): + for c in brevs[blo:bhi]: + ui.write('+%s\n' % c, label='diff.inserted') + if tag == 'equal': + for c in arevs[alo:ahi]: + ui.write(' %s\n' % c) + return 1 + + func = revset.makematcher(tree) revs = func(repo) if ui.verbose: ui.note(("* set:\n"), revset.prettyformatset(revs), "\n") @@ -3914,16 +3938,16 @@ [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')), ('0', 'print0', None, _('end filenames with NUL, for use with xargs')), ] + walkopts + formatteropts + subrepoopts, - _('[OPTION]... [PATTERN]...')) + _('[OPTION]... [FILE]...')) def files(ui, repo, *pats, **opts): """list tracked files Print files under Mercurial control in the working directory or - specified revision whose names match the given patterns (excluding - removed files). - - If no patterns are given to match, this command prints the names - of all files under Mercurial control in the working directory. + specified revision for given files (excluding removed files). + Files can be specified as filenames or filesets. + + If no files are given to match, this command prints the names + of all files under Mercurial control. .. container:: verbose @@ -3964,15 +3988,11 @@ end = '\n' if opts.get('print0'): end = '\0' - fm = ui.formatter('files', opts) fmt = '%s' + end m = scmutil.match(ctx, pats, opts) - ret = cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos')) - - fm.end() - - return ret + with ui.formatter('files', opts) as fm: + return cmdutil.files(ui, ctx, m, fm, fmt, opts.get('subrepos')) @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True) def forget(ui, repo, *pats, **opts): @@ -4142,9 +4162,7 @@ # check for ancestors of dest branch crev = repo['.'].rev() ancestors = repo.changelog.ancestors([crev], inclusive=True) - # Cannot use x.remove(y) on smart set, this has to be a list. # XXX make this lazy in the future - revs = list(revs) # don't mutate while iterating, create a copy for rev in list(revs): if rev in ancestors: @@ -4284,7 +4302,7 @@ _('only search files changed within revision range'), _('REV')), ('u', 'user', None, _('list the author (long with -v)')), ('d', 'date', None, _('list the date (short with -q)')), - ] + walkopts, + ] + formatteropts + walkopts, _('[OPTION]... PATTERN [FILE]...'), inferrepo=True) def grep(ui, repo, pattern, *pats, **opts): @@ -4349,19 +4367,16 @@ def __eq__(self, other): return self.line == other.line - def __iter__(self): - yield (self.line[:self.colstart], '') - yield (self.line[self.colstart:self.colend], 'grep.match') - rest = self.line[self.colend:] - while rest != '': - match = regexp.search(rest) - if not match: - yield (rest, '') + def findpos(self): + """Iterate all (start, end) indices of matches""" + yield self.colstart, self.colend + p = self.colend + while p < len(self.line): + m = regexp.search(self.line, p) + if not m: break - mstart, mend = match.span() - yield (rest[:mstart], '') - yield (rest[mstart:mend], 'grep.match') - rest = rest[mend:] + yield m.span() + p = m.end() matches = {} copies = {} @@ -4387,50 +4402,76 @@ for i in xrange(blo, bhi): yield ('+', b[i]) - def display(fn, ctx, pstates, states): + def display(fm, fn, ctx, pstates, states): rev = ctx.rev() + if fm.isplain(): + formatuser = ui.shortuser + else: + formatuser = str if ui.quiet: - datefunc = util.shortdate + datefmt = '%Y-%m-%d' else: - datefunc = util.datestr + datefmt = '%a %b %d %H:%M:%S %Y %1%2' found = False @util.cachefunc def binary(): flog = getfile(fn) return util.binary(flog.read(ctx.filenode(fn))) + fieldnamemap = {'filename': 'file', 'linenumber': 'line_number'} if opts.get('all'): iter = difflinestates(pstates, states) else: iter = [('', l) for l in states] for change, l in iter: - cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')] - - if opts.get('line_number'): - cols.append((str(l.linenum), 'grep.linenumber')) + fm.startitem() + fm.data(node=fm.hexfunc(ctx.node())) + cols = [ + ('filename', fn, True), + ('rev', rev, True), + ('linenumber', l.linenum, opts.get('line_number')), + ] if opts.get('all'): - cols.append((change, 'grep.change')) - if opts.get('user'): - cols.append((ui.shortuser(ctx.user()), 'grep.user')) - if opts.get('date'): - cols.append((datefunc(ctx.date()), 'grep.date')) - for col, label in cols[:-1]: - ui.write(col, label=label) - ui.write(sep, label='grep.sep') - ui.write(cols[-1][0], label=cols[-1][1]) + cols.append(('change', change, True)) + cols.extend([ + ('user', formatuser(ctx.user()), opts.get('user')), + ('date', fm.formatdate(ctx.date(), datefmt), opts.get('date')), + ]) + lastcol = next(name for name, data, cond in reversed(cols) if cond) + for name, data, cond in cols: + field = fieldnamemap.get(name, name) + fm.condwrite(cond, field, '%s', data, label='grep.%s' % name) + if cond and name != lastcol: + fm.plain(sep, label='grep.sep') if not opts.get('files_with_matches'): - ui.write(sep, label='grep.sep') + fm.plain(sep, label='grep.sep') if not opts.get('text') and binary(): - ui.write(_(" Binary file matches")) + fm.plain(_(" Binary file matches")) else: - for s, label in l: - ui.write(s, label=label) - ui.write(eol) + displaymatches(fm.nested('texts'), l) + fm.plain(eol) found = True if opts.get('files_with_matches'): break return found + def displaymatches(fm, l): + p = 0 + for s, e in l.findpos(): + if p < s: + fm.startitem() + fm.write('text', '%s', l.line[p:s]) + fm.data(matched=False) + fm.startitem() + fm.write('text', '%s', l.line[s:e], label='grep.match') + fm.data(matched=True) + p = e + if p < len(l.line): + fm.startitem() + fm.write('text', '%s', l.line[p:]) + fm.data(matched=False) + fm.end() + skip = {} revfiles = {} matchfn = scmutil.match(repo[None], pats, opts) @@ -4472,6 +4513,7 @@ except error.LookupError: pass + fm = ui.formatter('grep', opts) for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep): rev = ctx.rev() parent = ctx.p1().rev() @@ -4484,7 +4526,7 @@ continue pstates = matches.get(parent, {}).get(copy or fn, []) if pstates or states: - r = display(fn, ctx, pstates, states) + r = display(fm, fn, ctx, pstates, states) found = found or r if r and not opts.get('all'): skip[fn] = True @@ -4492,6 +4534,7 @@ skip[copy] = True del matches[rev] del revfiles[rev] + fm.end() return not found @@ -4607,12 +4650,15 @@ section = None subtopic = None if name and '.' in name: - name, section = name.split('.', 1) - section = encoding.lower(section) - if '.' in section: - subtopic, section = section.split('.', 1) + name, remaining = name.split('.', 1) + remaining = encoding.lower(remaining) + if '.' in remaining: + subtopic, section = remaining.split('.', 1) else: - subtopic = section + if name in help.subtopics: + subtopic = remaining + else: + section = remaining text = help.help_(ui, name, subtopic=subtopic, **opts) @@ -5437,7 +5483,9 @@ # ui.forcemerge is an internal variable, do not document repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge') force = opts.get('force') - return hg.merge(repo, node, force=force, mergeforce=force) + labels = ['working copy', 'merge rev'] + return hg.merge(repo, node, force=force, mergeforce=force, + labels=labels) finally: ui.setconfig('ui', 'forcemerge', '', 'merge') @@ -5611,10 +5659,10 @@ pathitems = sorted(ui.paths.iteritems()) fm = ui.formatter('paths', opts) - if fm: + if fm.isplain(): + hidepassword = util.hidepassword + else: hidepassword = str - else: - hidepassword = util.hidepassword if ui.quiet: namefmt = '%s\n' else: @@ -5936,7 +5984,7 @@ path = ui.paths.getpath(dest, default=('default-push', 'default')) if not path: raise error.Abort(_('default repository not configured!'), - hint=_('see the "path" section in "hg help config"')) + hint=_("see 'hg help config.paths'")) dest = path.pushloc or path.loc branches = (path.branch, opts.get('branch') or []) ui.status(_('pushing to %s\n') % util.hidepassword(dest)) @@ -6459,7 +6507,7 @@ ('n', 'name', '', _('name to show in web pages (default: working directory)'), _('NAME')), ('', 'web-conf', '', - _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')), + _("name of the hgweb config file (see 'hg help hgweb')"), _('FILE')), ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'), _('FILE')), ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')), @@ -7247,37 +7295,48 @@ """ return hg.verify(repo) -@command('version', [], norepo=True) -def version_(ui): +@command('version', [] + formatteropts, norepo=True) +def version_(ui, **opts): """output version and copyright information""" - ui.write(_("Mercurial Distributed SCM (version %s)\n") - % util.version()) - ui.status(_( + fm = ui.formatter("version", opts) + fm.startitem() + fm.write("ver", _("Mercurial Distributed SCM (version %s)\n"), + util.version()) + license = _( "(see https://mercurial-scm.org for more information)\n" "\nCopyright (C) 2005-2016 Matt Mackall and others\n" "This is free software; see the source for copying conditions. " "There is NO\nwarranty; " "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" - )) - - ui.note(_("\nEnabled extensions:\n\n")) + ) + if not ui.quiet: + fm.plain(license) + if ui.verbose: - # format names and versions into columns - names = [] - vers = [] - place = [] - for name, module in extensions.extensions(): - names.append(name) - vers.append(extensions.moduleversion(module)) - if extensions.ismoduleinternal(module): - place.append(_("internal")) - else: - place.append(_("external")) - if names: - maxnamelen = max(len(n) for n in names) - for i, name in enumerate(names): - ui.write(" %-*s %s %s\n" % - (maxnamelen, name, place[i], vers[i])) + fm.plain(_("\nEnabled extensions:\n\n")) + # format names and versions into columns + names = [] + vers = [] + isinternals = [] + for name, module in extensions.extensions(): + names.append(name) + vers.append(extensions.moduleversion(module) or None) + isinternals.append(extensions.ismoduleinternal(module)) + fn = fm.nested("extensions") + if names: + namefmt = " %%-%ds " % max(len(n) for n in names) + places = [_("external"), _("internal")] + for n, v, p in zip(names, vers, isinternals): + fn.startitem() + fn.condwrite(ui.verbose, "name", namefmt, n) + if ui.verbose: + fn.plain("%s " % places[p]) + fn.data(bundled=p) + fn.condwrite(ui.verbose and v, "ver", "%s", v) + if ui.verbose: + fn.plain("\n") + fn.end() + fm.end() def loadcmdtable(ui, name, cmdtable): """Load command functions from specified cmdtable
--- a/mercurial/context.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/context.py Tue Oct 18 14:15:15 2016 -0500 @@ -528,11 +528,12 @@ @propertycache def _manifest(self): - return self._repo.manifest.read(self._changeset.manifest) + return self._repo.manifestlog[self._changeset.manifest].read() @propertycache def _manifestdelta(self): - return self._repo.manifest.readdelta(self._changeset.manifest) + mfnode = self._changeset.manifest + return self._repo.manifestlog[mfnode].readdelta() @propertycache def _parents(self): @@ -823,7 +824,7 @@ """ repo = self._repo cl = repo.unfiltered().changelog - ma = repo.manifest + mfl = repo.manifestlog # fetch the linkrev fr = filelog.rev(fnode) lkr = filelog.linkrev(fr) @@ -848,7 +849,7 @@ if path in ac[3]: # checking the 'files' field. # The file has been touched, check if the content is # similar to the one we search for. - if fnode == ma.readfast(ac[0]).get(path): + if fnode == mfl[ac[0]].readfast().get(path): return a # In theory, we should never get out of that loop without a result. # But if manifest uses a buggy file revision (not children of the @@ -929,7 +930,7 @@ def lines(text): if text.endswith("\n"): return text.count("\n") - return text.count("\n") + 1 + return text.count("\n") + int(bool(text)) if linenumber: def decorate(text, rev): @@ -939,8 +940,7 @@ return ([(rev, False)] * lines(text), text) def pair(parent, child): - blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts, - refine=True) + blocks = mdiff.allblocks(parent[1], child[1], opts=diffopts) for (a1, a2, b1, b2), t in blocks: # Changed blocks ('!') or blocks made only of blank lines ('~') # belong to the child. @@ -1508,7 +1508,7 @@ # Only a case insensitive filesystem needs magic to translate user input # to actual case in the filesystem. - if not util.checkcase(r.root): + if not util.fscasesensitive(r.root): return matchmod.icasefsmatcher(r.root, r.getcwd(), pats, include, exclude, default, r.auditor, self, listsubrepos=listsubrepos,
--- a/mercurial/copies.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/copies.py Tue Oct 18 14:15:15 2016 -0500 @@ -231,30 +231,34 @@ return _chain(x, y, _backwardrenames(x, a), _forwardcopies(a, y, match=match)) -def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2): +def _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, baselabel=''): """Computes, based on addedinm1 and addedinm2, the files exclusive to c1 and c2. This is its own function so extensions can easily wrap this call to see what files mergecopies is about to process. Even though c1 and c2 are not used in this function, they are useful in other extensions for being able to read the file nodes of the changed files. + + "baselabel" can be passed to help distinguish the multiple computations + done in the graft case. """ u1 = sorted(addedinm1 - addedinm2) u2 = sorted(addedinm2 - addedinm1) + header = " unmatched files in %s" + if baselabel: + header += ' (from %s)' % baselabel if u1: - repo.ui.debug(" unmatched files in local:\n %s\n" - % "\n ".join(u1)) + repo.ui.debug("%s:\n %s\n" % (header % 'local', "\n ".join(u1))) if u2: - repo.ui.debug(" unmatched files in other:\n %s\n" - % "\n ".join(u2)) + repo.ui.debug("%s:\n %s\n" % (header % 'other', "\n ".join(u2))) return u1, u2 def _makegetfctx(ctx): - """return a 'getfctx' function suitable for checkcopies usage + """return a 'getfctx' function suitable for _checkcopies usage We have to re-setup the function building 'filectx' for each - 'checkcopies' to ensure the linkrev adjustment is properly setup for + '_checkcopies' to ensure the linkrev adjustment is properly setup for each. Linkrev adjustment is important to avoid bug in rename detection. Moreover, having a proper '_ancestrycontext' setup ensures the performance impact of this adjustment is kept limited. Without it, @@ -285,10 +289,26 @@ return fctx return util.lrucachefunc(makectx) -def mergecopies(repo, c1, c2, ca): +def _combinecopies(copyfrom, copyto, finalcopy, diverge, incompletediverge): + """combine partial copy paths""" + remainder = {} + for f in copyfrom: + if f in copyto: + finalcopy[copyto[f]] = copyfrom[f] + del copyto[f] + for f in incompletediverge: + assert f not in diverge + ic = incompletediverge[f] + if ic[0] in copyto: + diverge[f] = [copyto[ic[0]], ic[1]] + else: + remainder[f] = ic + return remainder + +def mergecopies(repo, c1, c2, base): """ Find moves and copies between context c1 and c2 that are relevant - for merging. + for merging. 'base' will be used as the merge base. Returns four dicts: "copy", "movewithdir", "diverge", and "renamedelete". @@ -321,6 +341,27 @@ if repo.ui.configbool('experimental', 'disablecopytrace'): return {}, {}, {}, {} + # In certain scenarios (e.g. graft, update or rebase), base can be + # overridden We still need to know a real common ancestor in this case We + # can't just compute _c1.ancestor(_c2) and compare it to ca, because there + # can be multiple common ancestors, e.g. in case of bidmerge. Because our + # caller may not know if the revision passed in lieu of the CA is a genuine + # common ancestor or not without explicitly checking it, it's better to + # determine that here. + # + # base.descendant(wc) and base.descendant(base) are False, work around that + _c1 = c1.p1() if c1.rev() is None else c1 + _c2 = c2.p1() if c2.rev() is None else c2 + # an endpoint is "dirty" if it isn't a descendant of the merge base + # if we have a dirty endpoint, we need to trigger graft logic, and also + # keep track of which endpoint is dirty + dirtyc1 = not (base == _c1 or base.descendant(_c1)) + dirtyc2 = not (base== _c2 or base.descendant(_c2)) + graft = dirtyc1 or dirtyc2 + tca = base + if graft: + tca = _c1.ancestor(_c2) + limit = _findlimit(repo, c1.rev(), c2.rev()) if limit is None: # no common ancestor, no copies @@ -329,28 +370,63 @@ m1 = c1.manifest() m2 = c2.manifest() - ma = ca.manifest() + mb = base.manifest() - copy1, copy2, = {}, {} - movewithdir1, movewithdir2 = {}, {} - fullcopy1, fullcopy2 = {}, {} - diverge = {} + # gather data from _checkcopies: + # - diverge = record all diverges in this dict + # - copy = record all non-divergent copies in this dict + # - fullcopy = record all copies in this dict + # - incomplete = record non-divergent partial copies here + # - incompletediverge = record divergent partial copies here + diverge = {} # divergence data is shared + incompletediverge = {} + data1 = {'copy': {}, + 'fullcopy': {}, + 'incomplete': {}, + 'diverge': diverge, + 'incompletediverge': incompletediverge, + } + data2 = {'copy': {}, + 'fullcopy': {}, + 'incomplete': {}, + 'diverge': diverge, + 'incompletediverge': incompletediverge, + } # find interesting file sets from manifests - addedinm1 = m1.filesnotin(ma) - addedinm2 = m2.filesnotin(ma) - u1, u2 = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2) + addedinm1 = m1.filesnotin(mb) + addedinm2 = m2.filesnotin(mb) bothnew = sorted(addedinm1 & addedinm2) + if tca == base: + # unmatched file from base + u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2) + u1u, u2u = u1r, u2r + else: + # unmatched file from base (DAG rotation in the graft case) + u1r, u2r = _computenonoverlap(repo, c1, c2, addedinm1, addedinm2, + baselabel='base') + # unmatched file from topological common ancestors (no DAG rotation) + # need to recompute this for directory move handling when grafting + mta = tca.manifest() + u1u, u2u = _computenonoverlap(repo, c1, c2, m1.filesnotin(mta), + m2.filesnotin(mta), + baselabel='topological common ancestor') - for f in u1: - checkcopies(c1, f, m1, m2, ca, limit, diverge, copy1, fullcopy1) + for f in u1u: + _checkcopies(c1, f, m1, m2, base, tca, dirtyc1, limit, data1) + + for f in u2u: + _checkcopies(c2, f, m2, m1, base, tca, dirtyc2, limit, data2) - for f in u2: - checkcopies(c2, f, m2, m1, ca, limit, diverge, copy2, fullcopy2) + copy = dict(data1['copy'].items() + data2['copy'].items()) + fullcopy = dict(data1['fullcopy'].items() + data2['fullcopy'].items()) - copy = dict(copy1.items() + copy2.items()) - movewithdir = dict(movewithdir1.items() + movewithdir2.items()) - fullcopy = dict(fullcopy1.items() + fullcopy2.items()) + if dirtyc1: + _combinecopies(data2['incomplete'], data1['incomplete'], copy, diverge, + incompletediverge) + else: + _combinecopies(data1['incomplete'], data2['incomplete'], copy, diverge, + incompletediverge) renamedelete = {} renamedeleteset = set() @@ -369,10 +445,44 @@ if bothnew: repo.ui.debug(" unmatched files new in both:\n %s\n" % "\n ".join(bothnew)) - bothdiverge, _copy, _fullcopy = {}, {}, {} + bothdiverge = {} + bothincompletediverge = {} + remainder = {} + both1 = {'copy': {}, + 'fullcopy': {}, + 'incomplete': {}, + 'diverge': bothdiverge, + 'incompletediverge': bothincompletediverge + } + both2 = {'copy': {}, + 'fullcopy': {}, + 'incomplete': {}, + 'diverge': bothdiverge, + 'incompletediverge': bothincompletediverge + } for f in bothnew: - checkcopies(c1, f, m1, m2, ca, limit, bothdiverge, _copy, _fullcopy) - checkcopies(c2, f, m2, m1, ca, limit, bothdiverge, _copy, _fullcopy) + _checkcopies(c1, f, m1, m2, base, tca, dirtyc1, limit, both1) + _checkcopies(c2, f, m2, m1, base, tca, dirtyc2, limit, both2) + if dirtyc1: + # incomplete copies may only be found on the "dirty" side for bothnew + assert not both2['incomplete'] + remainder = _combinecopies({}, both1['incomplete'], copy, bothdiverge, + bothincompletediverge) + elif dirtyc2: + assert not both1['incomplete'] + remainder = _combinecopies({}, both2['incomplete'], copy, bothdiverge, + bothincompletediverge) + else: + # incomplete copies and divergences can't happen outside grafts + assert not both1['incomplete'] + assert not both2['incomplete'] + assert not bothincompletediverge + for f in remainder: + assert f not in bothdiverge + ic = remainder[f] + if ic[0] in (m1 if dirtyc1 else m2): + # backed-out rename on one side, but watch out for deleted files + bothdiverge[f] = ic for of, fl in bothdiverge.items(): if len(fl) == 2 and fl[0] == fl[1]: copy[fl[0]] = of # not actually divergent, just matching renames @@ -393,7 +503,7 @@ del divergeset if not fullcopy: - return copy, movewithdir, diverge, renamedelete + return copy, {}, diverge, renamedelete repo.ui.debug(" checking for directory renames\n") @@ -431,14 +541,15 @@ del d1, d2, invalid if not dirmove: - return copy, movewithdir, diverge, renamedelete + return copy, {}, diverge, renamedelete for d in dirmove: repo.ui.debug(" discovered dir src: '%s' -> dst: '%s'\n" % (d, dirmove[d])) + movewithdir = {} # check unaccounted nonoverlapping files against directory moves - for f in u1 + u2: + for f in u1r + u2r: if f not in fullcopy: for d in dirmove: if f.startswith(d): @@ -452,55 +563,74 @@ return copy, movewithdir, diverge, renamedelete -def checkcopies(ctx, f, m1, m2, ca, limit, diverge, copy, fullcopy): +def _related(f1, f2, limit): + """return True if f1 and f2 filectx have a common ancestor + + Walk back to common ancestor to see if the two files originate + from the same file. Since workingfilectx's rev() is None it messes + up the integer comparison logic, hence the pre-step check for + None (f1 and f2 can only be workingfilectx's initially). + """ + + if f1 == f2: + return f1 # a match + + g1, g2 = f1.ancestors(), f2.ancestors() + try: + f1r, f2r = f1.linkrev(), f2.linkrev() + + if f1r is None: + f1 = next(g1) + if f2r is None: + f2 = next(g2) + + while True: + f1r, f2r = f1.linkrev(), f2.linkrev() + if f1r > f2r: + f1 = next(g1) + elif f2r > f1r: + f2 = next(g2) + elif f1 == f2: + return f1 # a match + elif f1r == f2r or f1r < limit or f2r < limit: + return False # copy no longer relevant + except StopIteration: + return False + +def _checkcopies(ctx, f, m1, m2, base, tca, remotebase, limit, data): """ check possible copies of f from m1 to m2 ctx = starting context for f in m1 - f = the filename to check + f = the filename to check (as in m1) m1 = the source manifest m2 = the destination manifest - ca = the changectx of the common ancestor + base = the changectx used as a merge base + tca = topological common ancestor for graft-like scenarios + remotebase = True if base is outside tca::ctx, False otherwise limit = the rev number to not search beyond - diverge = record all diverges in this dict - copy = record all non-divergent copies in this dict - fullcopy = record all copies in this dict + data = dictionary of dictionary to store copy data. (see mergecopies) + + note: limit is only an optimization, and there is no guarantee that + irrelevant revisions will not be limited + there is no easy way to make this algorithm stop in a guaranteed way + once it "goes behind a certain revision". """ - ma = ca.manifest() + mb = base.manifest() + mta = tca.manifest() + # Might be true if this call is about finding backward renames, + # This happens in the case of grafts because the DAG is then rotated. + # If the file exists in both the base and the source, we are not looking + # for a rename on the source side, but on the part of the DAG that is + # traversed backwards. + # + # In the case there is both backward and forward renames (before and after + # the base) this is more complicated as we must detect a divergence. + # We use 'backwards = False' in that case. + backwards = not remotebase and base != tca and f in mb getfctx = _makegetfctx(ctx) - def _related(f1, f2, limit): - # Walk back to common ancestor to see if the two files originate - # from the same file. Since workingfilectx's rev() is None it messes - # up the integer comparison logic, hence the pre-step check for - # None (f1 and f2 can only be workingfilectx's initially). - - if f1 == f2: - return f1 # a match - - g1, g2 = f1.ancestors(), f2.ancestors() - try: - f1r, f2r = f1.linkrev(), f2.linkrev() - - if f1r is None: - f1 = next(g1) - if f2r is None: - f2 = next(g2) - - while True: - f1r, f2r = f1.linkrev(), f2.linkrev() - if f1r > f2r: - f1 = next(g1) - elif f2r > f1r: - f2 = next(g2) - elif f1 == f2: - return f1 # a match - elif f1r == f2r or f1r < limit or f2r < limit: - return False # copy no longer relevant - except StopIteration: - return False - of = None seen = set([f]) for oc in getfctx(f, m1[f]).ancestors(): @@ -513,20 +643,47 @@ continue seen.add(of) - fullcopy[f] = of # remember for dir rename detection + # remember for dir rename detection + if backwards: + data['fullcopy'][of] = f # grafting backwards through renames + else: + data['fullcopy'][f] = of if of not in m2: continue # no match, keep looking - if m2[of] == ma.get(of): - break # no merge needed, quit early + if m2[of] == mb.get(of): + return # no merge needed, quit early c2 = getfctx(of, m2[of]) - cr = _related(oc, c2, ca.rev()) + # c2 might be a plain new file on added on destination side that is + # unrelated to the droids we are looking for. + cr = _related(oc, c2, tca.rev()) if cr and (of == f or of == c2.path()): # non-divergent - copy[f] = of - of = None - break + if backwards: + data['copy'][of] = f + elif of in mb: + data['copy'][f] = of + elif remotebase: # special case: a <- b <- a -> b "ping-pong" rename + data['copy'][of] = f + del data['fullcopy'][f] + data['fullcopy'][of] = f + else: # divergence w.r.t. graft CA on one side of topological CA + for sf in seen: + if sf in mb: + assert sf not in data['diverge'] + data['diverge'][sf] = [f, of] + break + return - if of in ma: - diverge.setdefault(of, []).append(f) + if of in mta: + if backwards or remotebase: + data['incomplete'][of] = f + else: + for sf in seen: + if sf in mb: + if tca == base: + data['diverge'].setdefault(sf, []).append(f) + else: + data['incompletediverge'][sf] = [of, f] + return def duplicatecopies(repo, rev, fromrev, skiprev=None): '''reproduce copies from fromrev to rev in the dirstate
--- a/mercurial/crecord.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/crecord.py Tue Oct 18 14:15:15 2016 -0500 @@ -28,7 +28,7 @@ # This is required for ncurses to display non-ASCII characters in default user # locale encoding correctly. --immerrr -locale.setlocale(locale.LC_ALL, '') +locale.setlocale(locale.LC_ALL, u'') # patch comments based on the git one diffhelptext = _("""# To remove '-' lines, make them ' ' lines (context). @@ -719,7 +719,7 @@ "scroll the screen to fully show the currently-selected" selstart = self.selecteditemstartline selend = self.selecteditemendline - #selnumlines = selend - selstart + padstart = self.firstlineofpadtoprint padend = padstart + self.yscreensize - self.numstatuslines - 1 # 'buffered' pad start/end values which scroll with a certain @@ -1263,7 +1263,6 @@ self.statuswin.resize(self.numstatuslines, self.xscreensize) self.numpadlines = self.getnumlinesdisplayed(ignorefolding=True) + 1 self.chunkpad = curses.newpad(self.numpadlines, self.xscreensize) - # todo: try to resize commit message window if possible except curses.error: pass @@ -1339,6 +1338,7 @@ shift-left-arrow [H] : go to parent header / fold selected header f : fold / unfold item, hiding/revealing its children F : fold / unfold parent item and all of its ancestors + ctrl-l : scroll the selected line to the top of the screen m : edit / resume editing the commit message e : edit the currently selected hunk a : toggle amend mode, only with commit -i @@ -1583,13 +1583,17 @@ self.helpwindow() self.stdscr.clear() self.stdscr.refresh() + elif curses.unctrl(keypressed) in ["^L"]: + # scroll the current line to the top of the screen + self.scrolllines(self.selecteditemstartline) def main(self, stdscr): """ method to be wrapped by curses.wrapper() for selecting chunks. """ - signal.signal(signal.SIGWINCH, self.sigwinchhandler) + origsigwinchhandler = signal.signal(signal.SIGWINCH, + self.sigwinchhandler) self.stdscr = stdscr # error during initialization, cannot be printed in the curses # interface, it should be printed by the calling code @@ -1640,3 +1644,4 @@ keypressed = "foobar" if self.handlekeypressed(keypressed): break + signal.signal(signal.SIGWINCH, origsigwinchhandler)
--- a/mercurial/demandimport.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/demandimport.py Tue Oct 18 14:15:15 2016 -0500 @@ -64,8 +64,12 @@ return importfunc(hgextname, globals, *args, **kwargs) class _demandmod(object): - """module demand-loader and proxy""" - def __init__(self, name, globals, locals, level=level): + """module demand-loader and proxy + + Specify 1 as 'level' argument at construction, to import module + relatively. + """ + def __init__(self, name, globals, locals, level): if '.' in name: head, rest = name.split('.', 1) after = [rest] @@ -117,7 +121,8 @@ if '.' in p: h, t = p.split('.', 1) if getattr(mod, h, nothing) is nothing: - setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__)) + setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__, + level=1)) elif t: subload(getattr(mod, h), t) @@ -186,11 +191,16 @@ def processfromitem(mod, attr): """Process an imported symbol in the import statement. - If the symbol doesn't exist in the parent module, it must be a - module. We set missing modules up as _demandmod instances. + If the symbol doesn't exist in the parent module, and if the + parent module is a package, it must be a module. We set missing + modules up as _demandmod instances. """ symbol = getattr(mod, attr, nothing) + nonpkg = getattr(mod, '__path__', nothing) is nothing if symbol is nothing: + if nonpkg: + # do not try relative import, which would raise ValueError + raise ImportError('cannot import name %s' % attr) mn = '%s.%s' % (mod.__name__, attr) if mn in ignore: importfunc = _origimport @@ -210,8 +220,8 @@ mod = rootmod for comp in modname.split('.')[1:]: if getattr(mod, comp, nothing) is nothing: - setattr(mod, comp, - _demandmod(comp, mod.__dict__, mod.__dict__)) + setattr(mod, comp, _demandmod(comp, mod.__dict__, + mod.__dict__, level=1)) mod = getattr(mod, comp) return mod @@ -259,6 +269,7 @@ '_imp', '_xmlplus', 'fcntl', + 'nt', # pathlib2 tests the existence of built-in 'nt' module 'win32com.gen_py', '_winreg', # 2.7 mimetypes needs immediate ImportError 'pythoncom', @@ -279,9 +290,17 @@ 'mimetools', 'sqlalchemy.events', # has import-time side effects (issue5085) # setuptools 8 expects this module to explode early when not on windows - 'distutils.msvc9compiler' + 'distutils.msvc9compiler', + '__builtin__', + 'builtins', ] +if _pypy: + ignore.extend([ + # _ctypes.pointer is shadowed by "from ... import pointer" (PyPy 5) + '_ctypes.pointer', + ]) + def isenabled(): return builtins.__import__ == _demandimport
--- a/mercurial/destutil.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/destutil.py Tue Oct 18 14:15:15 2016 -0500 @@ -416,8 +416,8 @@ 'updating to a closed head\n') % (currentbranch)) if otherheads: - ui.warn(_('(committing will reopen the head, ' - 'use `hg heads .` to see %i other heads)\n') % + ui.warn(_("(committing will reopen the head, " + "use 'hg heads .' to see %i other heads)\n") % (len(otherheads))) else: ui.warn(_('(committing will reopen branch "%s")\n') %
--- a/mercurial/dirs.c Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/dirs.c Tue Oct 18 14:15:15 2016 -0500 @@ -11,6 +11,12 @@ #include <Python.h> #include "util.h" +#ifdef IS_PY3K +#define PYLONG_VALUE(o) ((PyLongObject *)o)->ob_digit[1] +#else +#define PYLONG_VALUE(o) PyInt_AS_LONG(o) +#endif + /* * This is a multiset of directory names, built from the files that * appear in a dirstate or manifest. @@ -41,11 +47,20 @@ static int _addpath(PyObject *dirs, PyObject *path) { - const char *cpath = PyString_AS_STRING(path); - Py_ssize_t pos = PyString_GET_SIZE(path); + const char *cpath = PyBytes_AS_STRING(path); + Py_ssize_t pos = PyBytes_GET_SIZE(path); PyObject *key = NULL; int ret = -1; + /* This loop is super critical for performance. That's why we inline + * access to Python structs instead of going through a supported API. + * The implementation, therefore, is heavily dependent on CPython + * implementation details. We also commit violations of the Python + * "protocol" such as mutating immutable objects. But since we only + * mutate objects created in this function or in other well-defined + * locations, the references are known so these violations should go + * unnoticed. The code for adjusting the length of a PyBytesObject is + * essentially a minimal version of _PyBytes_Resize. */ while ((pos = _finddir(cpath, pos - 1)) != -1) { PyObject *val; @@ -53,30 +68,36 @@ in our dict. Try to avoid allocating and deallocating a string for each prefix we check. */ if (key != NULL) - ((PyStringObject *)key)->ob_shash = -1; + ((PyBytesObject *)key)->ob_shash = -1; else { /* Force Python to not reuse a small shared string. */ - key = PyString_FromStringAndSize(cpath, + key = PyBytes_FromStringAndSize(cpath, pos < 2 ? 2 : pos); if (key == NULL) goto bail; } - PyString_GET_SIZE(key) = pos; - PyString_AS_STRING(key)[pos] = '\0'; + /* Py_SIZE(o) refers to the ob_size member of the struct. Yes, + * assigning to what looks like a function seems wrong. */ + Py_SIZE(key) = pos; + ((PyBytesObject *)key)->ob_sval[pos] = '\0'; val = PyDict_GetItem(dirs, key); if (val != NULL) { - PyInt_AS_LONG(val) += 1; + PYLONG_VALUE(val) += 1; break; } /* Force Python to not reuse a small shared int. */ +#ifdef IS_PY3K + val = PyLong_FromLong(0x1eadbeef); +#else val = PyInt_FromLong(0x1eadbeef); +#endif if (val == NULL) goto bail; - PyInt_AS_LONG(val) = 1; + PYLONG_VALUE(val) = 1; ret = PyDict_SetItem(dirs, key, val); Py_DECREF(val); if (ret == -1) @@ -93,15 +114,15 @@ static int _delpath(PyObject *dirs, PyObject *path) { - char *cpath = PyString_AS_STRING(path); - Py_ssize_t pos = PyString_GET_SIZE(path); + char *cpath = PyBytes_AS_STRING(path); + Py_ssize_t pos = PyBytes_GET_SIZE(path); PyObject *key = NULL; int ret = -1; while ((pos = _finddir(cpath, pos - 1)) != -1) { PyObject *val; - key = PyString_FromStringAndSize(cpath, pos); + key = PyBytes_FromStringAndSize(cpath, pos); if (key == NULL) goto bail; @@ -113,7 +134,7 @@ goto bail; } - if (--PyInt_AS_LONG(val) <= 0) { + if (--PYLONG_VALUE(val) <= 0) { if (PyDict_DelItem(dirs, key) == -1) goto bail; } else @@ -134,7 +155,7 @@ Py_ssize_t pos = 0; while (PyDict_Next(source, &pos, &key, &value)) { - if (!PyString_Check(key)) { + if (!PyBytes_Check(key)) { PyErr_SetString(PyExc_TypeError, "expected string key"); return -1; } @@ -165,7 +186,7 @@ return -1; while ((item = PyIter_Next(iter)) != NULL) { - if (!PyString_Check(item)) { + if (!PyBytes_Check(item)) { PyErr_SetString(PyExc_TypeError, "expected string"); break; } @@ -224,7 +245,7 @@ { PyObject *path; - if (!PyArg_ParseTuple(args, "O!:addpath", &PyString_Type, &path)) + if (!PyArg_ParseTuple(args, "O!:addpath", &PyBytes_Type, &path)) return NULL; if (_addpath(self->dict, path) == -1) @@ -237,7 +258,7 @@ { PyObject *path; - if (!PyArg_ParseTuple(args, "O!:delpath", &PyString_Type, &path)) + if (!PyArg_ParseTuple(args, "O!:delpath", &PyBytes_Type, &path)) return NULL; if (_delpath(self->dict, path) == -1) @@ -248,7 +269,7 @@ static int dirs_contains(dirsObject *self, PyObject *value) { - return PyString_Check(value) ? PyDict_Contains(self->dict, value) : 0; + return PyBytes_Check(value) ? PyDict_Contains(self->dict, value) : 0; } static void dirs_dealloc(dirsObject *self) @@ -270,7 +291,7 @@ {NULL} /* Sentinel */ }; -static PyTypeObject dirsType = { PyObject_HEAD_INIT(NULL) }; +static PyTypeObject dirsType = { PyVarObject_HEAD_INIT(NULL, 0) }; void dirs_module_init(PyObject *mod) {
--- a/mercurial/dirstate.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/dirstate.py Tue Oct 18 14:15:15 2016 -0500 @@ -74,8 +74,6 @@ raise return (vfs(filename), False) -_token = object() - class dirstate(object): def __init__(self, opener, ui, root, validate): @@ -103,6 +101,8 @@ self._parentwriters = 0 self._filename = 'dirstate' self._pendingfilename = '%s.pending' % self._filename + self._plchangecallbacks = {} + self._origpl = None # for consistent view between _pl() and _read() invocations self._pendingmode = None @@ -227,7 +227,7 @@ @propertycache def _checkcase(self): - return not util.checkcase(self._join('.hg')) + return not util.fscasesensitive(self._join('.hg')) def _join(self, f): # much faster than os.path.join() @@ -349,6 +349,8 @@ self._dirty = self._dirtypl = True oldp2 = self._pl[1] + if self._origpl is None: + self._origpl = self._pl self._pl = p1, p2 copies = {} if oldp2 != nullid and p2 == nullid: @@ -444,6 +446,7 @@ self._lastnormaltime = 0 self._dirty = False self._parentwriters = 0 + self._origpl = None def copy(self, source, dest): """Mark dest as a copy of source. Unmark dest if source is None.""" @@ -677,37 +680,23 @@ self.clear() self._lastnormaltime = lastnormaltime + if self._origpl is None: + self._origpl = self._pl + self._pl = (parent, nullid) for f in changedfiles: - mode = 0o666 - if f in allfiles and 'x' in allfiles.flags(f): - mode = 0o777 - if f in allfiles: - self._map[f] = dirstatetuple('n', mode, -1, 0) + self.normallookup(f) else: - self._map.pop(f, None) - if f in self._nonnormalset: - self._nonnormalset.remove(f) + self.drop(f) - self._pl = (parent, nullid) self._dirty = True - def write(self, tr=_token): + def write(self, tr): if not self._dirty: return filename = self._filename - 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 - # should be written into it, because it has priority - # to '.hg/dirstate' at reading under HG_PENDING mode - filename = self._pendingfilename - elif tr: + if tr: # 'dirstate.write()' is not only for writing in-memory # changes out, but also for dropping ambiguous timestamp. # delayed writing re-raise "ambiguous timestamp issue". @@ -733,7 +722,23 @@ st = self._opener(filename, "w", atomictemp=True, checkambig=True) self._writedirstate(st) + def addparentchangecallback(self, category, callback): + """add a callback to be called when the wd parents are changed + + Callback will be called with the following arguments: + dirstate, (oldp1, oldp2), (newp1, newp2) + + Category is a unique identifier to allow overwriting an old callback + with a newer callback. + """ + self._plchangecallbacks[category] = callback + def _writedirstate(self, st): + # notify callbacks about parents change + if self._origpl is not None and self._origpl != self._pl: + for c, callback in sorted(self._plchangecallbacks.iteritems()): + callback(self, self._origpl, self._pl) + self._origpl = None # use the modification time of the newly created temporary file as the # filesystem's notion of 'now' now = util.fstat(st).st_mtime & _rangemask
--- a/mercurial/discovery.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/discovery.py Tue Oct 18 14:15:15 2016 -0500 @@ -76,10 +76,29 @@ The sets are computed on demand from the heads, unless provided upfront by discovery.''' - def __init__(self, revlog, commonheads, missingheads): + def __init__(self, repo, commonheads=None, missingheads=None, + missingroots=None): + # at least one of them must not be set + assert None in (commonheads, missingroots) + cl = repo.changelog + if missingheads is None: + missingheads = cl.heads() + if missingroots: + discbases = [] + for n in missingroots: + discbases.extend([p for p in cl.parents(n) if p != nullid]) + # TODO remove call to nodesbetween. + # TODO populate attributes on outgoing instance instead of setting + # discbases. + csets, roots, heads = cl.nodesbetween(missingroots, missingheads) + included = set(csets) + missingheads = heads + commonheads = [n for n in discbases if n not in included] + elif not commonheads: + commonheads = [nullid] self.commonheads = commonheads self.missingheads = missingheads - self._revlog = revlog + self._revlog = cl self._common = None self._missing = None self.excluded = [] @@ -116,7 +135,7 @@ If portable is given, compute more conservative common and missingheads, to make bundles created from the instance more portable.''' # declare an empty outgoing object to be filled later - og = outgoing(repo.changelog, None, None) + og = outgoing(repo, None, None) # get common set if not provided if commoninc is None: @@ -382,7 +401,7 @@ errormsg = (_("push creates new branch '%s' " "with multiple heads") % (branch)) hint = _("merge or" - " see \"hg help push\" for details about" + " see 'hg help push' for details about" " pushing new heads") elif len(newhs) > len(oldhs): # remove bookmarked or existing remote heads from the new heads list @@ -401,11 +420,11 @@ ) % short(dhs[0]) if unsyncedheads: hint = _("pull and merge or" - " see \"hg help push\" for details about" + " see 'hg help push' for details about" " pushing new heads") else: hint = _("merge or" - " see \"hg help push\" for details about" + " see 'hg help push' for details about" " pushing new heads") if branch is None: repo.ui.note(_("new remote heads:\n"))
--- a/mercurial/dispatch.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/dispatch.py Tue Oct 18 14:15:15 2016 -0500 @@ -34,6 +34,7 @@ fileset, hg, hook, + profiling, revset, templatefilters, templatekw, @@ -150,7 +151,7 @@ except ValueError: pass # happens if called in a thread - try: + def _runcatchfunc(): try: debugger = 'pdb' debugtrace = { @@ -212,6 +213,16 @@ ui.traceback() raise + return callcatch(ui, _runcatchfunc) + +def callcatch(ui, func): + """call func() with global exception handling + + return func() if no exception happens. otherwise do some error handling + and return an exit code accordingly. + """ + try: + return func() # Global exception handling, alphabetically # Mercurial-specific first, followed by built-in and library exceptions except error.AmbiguousCommand as inst: @@ -489,6 +500,8 @@ ui.debug("alias '%s' shadows command '%s'\n" % (self.name, self.cmdname)) + ui.log('commandalias', "alias '%s' expands to '%s'\n", + self.name, self.definition) if util.safehasattr(self, 'shell'): return self.fn(ui, *args, **opts) else: @@ -545,7 +558,7 @@ c.append((o[0], o[1], options[o[1]], o[3])) try: - args = fancyopts.fancyopts(args, c, cmdoptions, True) + args = fancyopts.fancyopts(args, c, cmdoptions, gnu=True) except fancyopts.getopt.GetoptError as inst: raise error.CommandError(cmd, inst) @@ -761,7 +774,8 @@ # Check abbreviation/ambiguity of shell alias. shellaliasfn = _checkshellalias(lui, ui, args) if shellaliasfn: - return shellaliasfn() + with profiling.maybeprofile(lui): + return shellaliasfn() # check for fallback encoding fallback = lui.config('ui', 'fallbackencoding') @@ -808,6 +822,10 @@ for ui_ in uis: ui_.setconfig('ui', opt, val, '--' + opt) + if options['profile']: + for ui_ in uis: + ui_.setconfig('profiling', 'enabled', 'true', '--profile') + if options['traceback']: for ui_ in uis: ui_.setconfig('ui', 'traceback', 'on', '--traceback') @@ -827,187 +845,70 @@ elif not cmd: return commands.help_(ui, 'shortlist') - repo = None - cmdpats = args[:] - if not _cmdattr(ui, cmd, func, 'norepo'): - # use the repo from the request only if we don't have -R - if not rpath and not cwd: - repo = req.repo - - if repo: - # set the descriptors of the repo ui to those of ui - repo.ui.fin = ui.fin - repo.ui.fout = ui.fout - repo.ui.ferr = ui.ferr - else: - try: - repo = hg.repository(ui, path=path) - if not repo.local(): - raise error.Abort(_("repository '%s' is not local") % path) - repo.ui.setconfig("bundle", "mainreporoot", repo.root, 'repo') - except error.RequirementError: - raise - except error.RepoError: - if rpath and rpath[-1]: # invalid -R path - raise - if not _cmdattr(ui, cmd, func, 'optionalrepo'): - if (_cmdattr(ui, cmd, func, 'inferrepo') and - args and not path): - # try to infer -R from command args - repos = map(cmdutil.findrepo, args) - guess = repos[0] - if guess and repos.count(guess) == len(repos): - req.args = ['--repository', guess] + fullargs - return _dispatch(req) - if not path: - raise error.RepoError(_("no repository found in '%s'" - " (.hg not found)") - % os.getcwd()) - raise - if repo: - ui = repo.ui - if options['hidden']: - repo = repo.unfiltered() - args.insert(0, repo) - elif rpath: - ui.warn(_("warning: --repository ignored\n")) - - msg = ' '.join(' ' in a and repr(a) or a for a in fullargs) - ui.log("command", '%s\n', msg) - d = lambda: util.checksignature(func)(ui, *args, **cmdoptions) - try: - return runcommand(lui, repo, cmd, fullargs, ui, options, d, - cmdpats, cmdoptions) - finally: - if repo and repo != req.repo: - repo.close() - -def lsprofile(ui, func, fp): - format = ui.config('profiling', 'format', default='text') - field = ui.config('profiling', 'sort', default='inlinetime') - limit = ui.configint('profiling', 'limit', default=30) - climit = ui.configint('profiling', 'nested', default=0) - - if format not in ['text', 'kcachegrind']: - ui.warn(_("unrecognized profiling format '%s'" - " - Ignored\n") % format) - format = 'text' + with profiling.maybeprofile(lui): + repo = None + cmdpats = args[:] + if not _cmdattr(ui, cmd, func, 'norepo'): + # use the repo from the request only if we don't have -R + if not rpath and not cwd: + repo = req.repo - try: - from . import lsprof - except ImportError: - raise error.Abort(_( - 'lsprof not available - install from ' - 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/')) - p = lsprof.Profiler() - p.enable(subcalls=True) - try: - return func() - finally: - p.disable() - - if format == 'kcachegrind': - from . import lsprofcalltree - calltree = lsprofcalltree.KCacheGrind(p) - calltree.output(fp) - else: - # format == 'text' - stats = lsprof.Stats(p.getstats()) - stats.sort(field) - stats.pprint(limit=limit, file=fp, climit=climit) + if repo: + # set the descriptors of the repo ui to those of ui + repo.ui.fin = ui.fin + repo.ui.fout = ui.fout + repo.ui.ferr = ui.ferr + else: + try: + repo = hg.repository(ui, path=path) + if not repo.local(): + raise error.Abort(_("repository '%s' is not local") + % path) + repo.ui.setconfig("bundle", "mainreporoot", repo.root, + 'repo') + except error.RequirementError: + raise + except error.RepoError: + if rpath and rpath[-1]: # invalid -R path + raise + if not _cmdattr(ui, cmd, func, 'optionalrepo'): + if (_cmdattr(ui, cmd, func, 'inferrepo') and + args and not path): + # try to infer -R from command args + repos = map(cmdutil.findrepo, args) + guess = repos[0] + if guess and repos.count(guess) == len(repos): + req.args = ['--repository', guess] + fullargs + return _dispatch(req) + if not path: + raise error.RepoError(_("no repository found in" + " '%s' (.hg not found)") + % os.getcwd()) + raise + if repo: + ui = repo.ui + if options['hidden']: + repo = repo.unfiltered() + args.insert(0, repo) + elif rpath: + ui.warn(_("warning: --repository ignored\n")) -def flameprofile(ui, func, fp): - try: - from flamegraph import flamegraph - except ImportError: - raise error.Abort(_( - 'flamegraph not available - install from ' - 'https://github.com/evanhempel/python-flamegraph')) - # developer config: profiling.freq - freq = ui.configint('profiling', 'freq', default=1000) - filter_ = None - collapse_recursion = True - thread = flamegraph.ProfileThread(fp, 1.0 / freq, - filter_, collapse_recursion) - start_time = time.clock() - try: - thread.start() - func() - finally: - thread.stop() - thread.join() - print('Collected %d stack frames (%d unique) in %2.2f seconds.' % ( - time.clock() - start_time, thread.num_frames(), - thread.num_frames(unique=True))) - - -def statprofile(ui, func, fp): - try: - import statprof - except ImportError: - raise error.Abort(_( - 'statprof not available - install using "easy_install statprof"')) - - freq = ui.configint('profiling', 'freq', default=1000) - if freq > 0: - statprof.reset(freq) - else: - ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq) - - statprof.start() - try: - return func() - finally: - statprof.stop() - statprof.display(fp) + msg = ' '.join(' ' in a and repr(a) or a for a in fullargs) + ui.log("command", '%s\n', msg) + d = lambda: util.checksignature(func)(ui, *args, **cmdoptions) + try: + return runcommand(lui, repo, cmd, fullargs, ui, options, d, + cmdpats, cmdoptions) + finally: + if repo and repo != req.repo: + repo.close() def _runcommand(ui, options, cmd, cmdfunc): - """Enables the profiler if applicable. - - ``profiling.enabled`` - boolean config that enables or disables profiling - """ - def checkargs(): - try: - return cmdfunc() - except error.SignatureError: - raise error.CommandError(cmd, _("invalid arguments")) - - if options['profile'] or ui.configbool('profiling', 'enabled'): - profiler = os.getenv('HGPROF') - if profiler is None: - profiler = ui.config('profiling', 'type', default='ls') - if profiler not in ('ls', 'stat', 'flame'): - ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler) - profiler = 'ls' - - output = ui.config('profiling', 'output') - - if output == 'blackbox': - fp = util.stringio() - elif output: - path = ui.expandpath(output) - fp = open(path, 'wb') - else: - fp = sys.stderr - - try: - if profiler == 'ls': - return lsprofile(ui, checkargs, fp) - elif profiler == 'flame': - return flameprofile(ui, checkargs, fp) - else: - return statprofile(ui, checkargs, fp) - finally: - if output: - if output == 'blackbox': - val = "Profile:\n%s" % fp.getvalue() - # ui.log treats the input as a format string, - # so we need to escape any % signs. - val = val.replace('%', '%%') - ui.log('profile', val) - fp.close() - else: - return checkargs() + """Run a command function, possibly with profiling enabled.""" + try: + return cmdfunc() + except error.SignatureError: + raise error.CommandError(cmd, _('invalid arguments')) def _exceptionwarning(ui): """Produce a warning message for the current active exception""" @@ -1031,7 +932,7 @@ break # Never blame on extensions bundled with Mercurial. - if testedwith == 'internal': + if extensions.ismoduleinternal(mod): continue tested = [util.versiontuple(t, 2) for t in testedwith.split()]
--- a/mercurial/encoding.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/encoding.py Tue Oct 18 14:15:15 2016 -0500 @@ -10,14 +10,16 @@ import array import locale import os -import sys import unicodedata from . import ( error, + pycompat, ) -if sys.version_info[0] >= 3: +_sysstr = pycompat.sysstr + +if pycompat.ispy3: unichr = chr # These unicode characters are ignored by HFS+ (Apple Technote 1150, @@ -27,7 +29,7 @@ "200c 200d 200e 200f 202a 202b 202c 202d 202e " "206a 206b 206c 206d 206e 206f feff".split()] # verify the next function will work -if sys.version_info[0] >= 3: +if pycompat.ispy3: assert set(i[0] for i in _ignore) == set([ord(b'\xe2'), ord(b'\xef')]) else: assert set(i[0] for i in _ignore) == set(["\xe2", "\xef"]) @@ -45,6 +47,19 @@ s = s.replace(c, '') return s +# encoding.environ is provided read-only, which may not be used to modify +# the process environment +_nativeenviron = (not pycompat.ispy3 or os.supports_bytes_environ) +if not pycompat.ispy3: + environ = os.environ +elif _nativeenviron: + environ = os.environb +else: + # preferred encoding isn't known yet; use utf-8 to avoid unicode error + # and recreate it once encoding is settled + environ = dict((k.encode(u'utf-8'), v.encode(u'utf-8')) + for k, v in os.environ.items()) + def _getpreferredencoding(): ''' On darwin, getpreferredencoding ignores the locale environment and @@ -76,13 +91,13 @@ } try: - encoding = os.environ.get("HGENCODING") + encoding = environ.get("HGENCODING") if not encoding: encoding = locale.getpreferredencoding() or 'ascii' encoding = _encodingfixers.get(encoding, lambda: encoding)() except locale.Error: encoding = 'ascii' -encodingmode = os.environ.get("HGENCODINGMODE", "strict") +encodingmode = environ.get("HGENCODINGMODE", "strict") fallbackencoding = 'ISO-8859-1' class localstr(str): @@ -136,23 +151,24 @@ if encoding == 'UTF-8': # fast path return s - r = u.encode(encoding, "replace") - if u == r.decode(encoding): + r = u.encode(_sysstr(encoding), u"replace") + if u == r.decode(_sysstr(encoding)): # r is a safe, non-lossy encoding of s return r return localstr(s, r) except UnicodeDecodeError: # we should only get here if we're looking at an ancient changeset try: - u = s.decode(fallbackencoding) - r = u.encode(encoding, "replace") - if u == r.decode(encoding): + u = s.decode(_sysstr(fallbackencoding)) + r = u.encode(_sysstr(encoding), u"replace") + if u == r.decode(_sysstr(encoding)): # r is a safe, non-lossy encoding of s return r return localstr(u.encode('UTF-8'), r) except UnicodeDecodeError: u = s.decode("utf-8", "replace") # last ditch - return u.encode(encoding, "replace") # can't round-trip + # can't round-trip + return u.encode(_sysstr(encoding), u"replace") except LookupError as k: raise error.Abort(k, hint="please check your locale settings") @@ -172,20 +188,27 @@ return s._utf8 try: - return s.decode(encoding, encodingmode).encode("utf-8") + u = s.decode(_sysstr(encoding), _sysstr(encodingmode)) + return u.encode("utf-8") except UnicodeDecodeError as inst: sub = s[max(0, inst.start - 10):inst.start + 10] raise error.Abort("decoding near '%s': %s!" % (sub, inst)) except LookupError as k: raise error.Abort(k, hint="please check your locale settings") +if not _nativeenviron: + # now encoding and helper functions are available, recreate the environ + # dict to be exported to other modules + environ = dict((tolocal(k.encode(u'utf-8')), tolocal(v.encode(u'utf-8'))) + for k, v in os.environ.items()) + # How to treat ambiguous-width characters. Set to 'wide' to treat as wide. -wide = (os.environ.get("HGENCODINGAMBIGUOUS", "narrow") == "wide" +wide = (environ.get("HGENCODINGAMBIGUOUS", "narrow") == "wide" and "WFA" or "WF") def colwidth(s): "Find the column width of a string for display in the local encoding" - return ucolwidth(s.decode(encoding, 'replace')) + return ucolwidth(s.decode(_sysstr(encoding), u'replace')) def ucolwidth(d): "Find the column width of a Unicode string for display" @@ -265,7 +288,7 @@ + """ try: - u = s.decode(encoding) + u = s.decode(_sysstr(encoding)) except UnicodeDecodeError: if len(s) <= width: # trimming is not needed return s @@ -292,7 +315,7 @@ for i in xrange(1, len(u)): usub = uslice(i) if ucolwidth(usub) <= width: - return concat(usub.encode(encoding)) + return concat(usub.encode(_sysstr(encoding))) return ellipsis # no enough room for multi-column characters def _asciilower(s): @@ -337,12 +360,12 @@ if isinstance(s, localstr): u = s._utf8.decode("utf-8") else: - u = s.decode(encoding, encodingmode) + u = s.decode(_sysstr(encoding), _sysstr(encodingmode)) lu = u.lower() if u == lu: return s # preserve localstring - return lu.encode(encoding) + return lu.encode(_sysstr(encoding)) except UnicodeError: return s.lower() # we don't know how to fold this except in ASCII except LookupError as k: @@ -360,12 +383,12 @@ if isinstance(s, localstr): u = s._utf8.decode("utf-8") else: - u = s.decode(encoding, encodingmode) + u = s.decode(_sysstr(encoding), _sysstr(encodingmode)) uu = u.upper() if u == uu: return s # preserve localstring - return uu.encode(encoding) + return uu.encode(_sysstr(encoding)) except UnicodeError: return s.upper() # we don't know how to fold this except in ASCII except LookupError as k:
--- a/mercurial/exchange.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/exchange.py Tue Oct 18 14:15:15 2016 -0500 @@ -257,13 +257,40 @@ return bundler.newpart('obsmarkers', data=stream) return None -def _canusebundle2(op): - """return true if a pull/push can use bundle2 +def _computeoutgoing(repo, heads, common): + """Computes which revs are outgoing given a set of common + and a set of heads. + + This is a separate function so extensions can have access to + the logic. - Feel free to nuke this function when we drop the experimental option""" - return (op.repo.ui.configbool('experimental', 'bundle2-exp', True) - and op.remote.capable('bundle2')) + Returns a discovery.outgoing object. + """ + cl = repo.changelog + if common: + hasnode = cl.hasnode + common = [n for n in common if hasnode(n)] + else: + common = [nullid] + if not heads: + heads = cl.heads() + return discovery.outgoing(repo, common, heads) +def _forcebundle1(op): + """return true if a pull/push must use bundle1 + + This function is used to allow testing of the older bundle version""" + ui = op.repo.ui + forcebundle1 = False + # The goal is this config is to allow developper to choose the bundle + # version used during exchanged. This is especially handy during test. + # Value is a list of bundle version to be picked from, highest version + # should be used. + # + # developer config: devel.legacy.exchange + exchange = ui.configlist('devel', 'legacy.exchange') + forcebundle1 = 'bundle2' not in exchange and 'bundle1' in exchange + return forcebundle1 or not op.remote.capable('bundle2') class pushoperation(object): """A object that represent a single push operation @@ -417,7 +444,7 @@ # bundle2 push may receive a reply bundle touching bookmarks or other # things requiring the wlock. Take it now to ensure proper ordering. maypushback = pushop.ui.configbool('experimental', 'bundle2.pushback') - if _canusebundle2(pushop) and maypushback: + if (not _forcebundle1(pushop)) and maypushback: localwlock = pushop.repo.wlock() locallock = pushop.repo.lock() pushop.locallocked = True @@ -442,7 +469,7 @@ lock = pushop.remote.lock() try: _pushdiscovery(pushop) - if _canusebundle2(pushop): + if not _forcebundle1(pushop): _pushbundle2(pushop) _pushchangeset(pushop) _pushsyncphase(pushop) @@ -1100,7 +1127,7 @@ @util.propertycache def canusebundle2(self): - return _canusebundle2(self) + return not _forcebundle1(self) @util.propertycache def remotebundle2caps(self): @@ -1174,8 +1201,10 @@ " %s") % (', '.join(sorted(missing))) raise error.Abort(msg) - lock = pullop.repo.lock() + wlock = lock = None try: + wlock = pullop.repo.wlock() + lock = pullop.repo.lock() pullop.trmanager = transactionmanager(repo, 'pull', remote.url()) streamclone.maybeperformlegacystreamclone(pullop) # This should ideally be in _pullbundle2(). However, it needs to run @@ -1190,8 +1219,7 @@ _pullobsolete(pullop) pullop.trmanager.close() finally: - pullop.trmanager.release() - lock.release() + lockmod.release(pullop.trmanager, lock, wlock) return pullop @@ -1504,20 +1532,14 @@ return any(cap.startswith('HG2') for cap in bundlecaps) return False -def getbundle(repo, source, heads=None, common=None, bundlecaps=None, - **kwargs): - """return a full bundle (with potentially multiple kind of parts) +def getbundlechunks(repo, source, heads=None, common=None, bundlecaps=None, + **kwargs): + """Return chunks constituting a bundle's raw data. Could be a bundle HG10 or a bundle HG20 depending on bundlecaps - passed. For now, the bundle can contain only changegroup, but this will - changes when more part type will be available for bundle2. + passed. - This is different from changegroup.getchangegroup that only returns an HG10 - changegroup bundle. They may eventually get reunited in the future when we - have a clearer idea of the API we what to query different data. - - The implementation is at a very early stage and will get massive rework - when the API of bundle is refined. + Returns an iterator over raw chunks (of varying sizes). """ usebundle2 = bundle2requested(bundlecaps) # bundle10 case @@ -1528,8 +1550,9 @@ if kwargs: raise ValueError(_('unsupported getbundle arguments: %s') % ', '.join(sorted(kwargs.keys()))) - return changegroup.getchangegroup(repo, source, heads=heads, - common=common, bundlecaps=bundlecaps) + outgoing = _computeoutgoing(repo, heads, common) + bundler = changegroup.getbundler('01', repo, bundlecaps) + return changegroup.getsubsetraw(repo, outgoing, bundler, source) # bundle20 case b2caps = {} @@ -1547,7 +1570,7 @@ func(bundler, repo, source, bundlecaps=bundlecaps, b2caps=b2caps, **kwargs) - return util.chunkbuffer(bundler.getchunks()) + return bundler.getchunks() @getbundle2partsgenerator('changegroup') def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None, @@ -1564,7 +1587,7 @@ if not cgversions: raise ValueError(_('no common changegroup version')) version = max(cgversions) - outgoing = changegroup.computeoutgoing(repo, heads, common) + outgoing = _computeoutgoing(repo, heads, common) cg = changegroup.getlocalchangegroupraw(repo, source, outgoing, bundlecaps=bundlecaps, version=version) @@ -1617,7 +1640,7 @@ if not (kwargs.get('cg', True) and 'hgtagsfnodes' in b2caps): return - outgoing = changegroup.computeoutgoing(repo, heads, common) + outgoing = _computeoutgoing(repo, heads, common) if not outgoing.missingheads: return
--- a/mercurial/extensions.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/extensions.py Tue Oct 18 14:15:15 2016 -0500 @@ -22,6 +22,7 @@ ) _extensions = {} +_disabledextensions = {} _aftercallbacks = {} _order = [] _builtin = set(['hbisect', 'bookmarks', 'parentrevspec', 'progress', 'interhg', @@ -79,7 +80,29 @@ mod = getattr(mod, comp) return mod +def _importext(name, path=None, reportfunc=None): + if path: + # the module will be loaded in sys.modules + # choose an unique name so that it doesn't + # conflicts with other modules + mod = loadpath(path, 'hgext.%s' % name) + else: + try: + mod = _importh("hgext.%s" % name) + except ImportError as err: + if reportfunc: + reportfunc(err, "hgext.%s" % name, "hgext3rd.%s" % name) + try: + mod = _importh("hgext3rd.%s" % name) + except ImportError as err: + if reportfunc: + reportfunc(err, "hgext3rd.%s" % name, name) + mod = _importh(name) + return mod + def _reportimporterror(ui, err, failed, next): + # note: this ui.debug happens before --debug is processed, + # Use --config ui.debug=1 to see them. ui.debug('could not import %s (%s): trying %s\n' % (failed, err, next)) if ui.debugflag: @@ -95,21 +118,7 @@ if shortname in _extensions: return _extensions[shortname] _extensions[shortname] = None - if path: - # the module will be loaded in sys.modules - # choose an unique name so that it doesn't - # conflicts with other modules - mod = loadpath(path, 'hgext.%s' % name) - else: - try: - mod = _importh("hgext.%s" % name) - except ImportError as err: - _reportimporterror(ui, err, "hgext.%s" % name, name) - try: - mod = _importh("hgext3rd.%s" % name) - except ImportError as err: - _reportimporterror(ui, err, "hgext3rd.%s" % name, name) - mod = _importh(name) + mod = _importext(name, path, bind(_reportimporterror, ui)) # Before we do anything with the extension, check against minimum stated # compatibility. This gives extension authors a mechanism to have their @@ -148,6 +157,7 @@ for (name, path) in result: if path: if path[0] == '!': + _disabledextensions[name] = path[1:] continue try: load(ui, name, path) @@ -210,11 +220,13 @@ return func(*(args + a), **kw) return closure -def _updatewrapper(wrap, origfn): - '''Copy attributes to wrapper function''' +def _updatewrapper(wrap, origfn, unboundwrapper): + '''Copy and add some useful attributes to wrapper''' wrap.__module__ = getattr(origfn, '__module__') wrap.__doc__ = getattr(origfn, '__doc__') wrap.__dict__.update(getattr(origfn, '__dict__', {})) + wrap._origfunc = origfn + wrap._unboundwrapper = unboundwrapper def wrapcommand(table, command, wrapper, synopsis=None, docstring=None): '''Wrap the command named `command' in table @@ -254,7 +266,7 @@ origfn = entry[0] wrap = bind(util.checksignature(wrapper), util.checksignature(origfn)) - _updatewrapper(wrap, origfn) + _updatewrapper(wrap, origfn, wrapper) if docstring is not None: wrap.__doc__ += docstring @@ -303,10 +315,46 @@ origfn = getattr(container, funcname) assert callable(origfn) wrap = bind(wrapper, origfn) - _updatewrapper(wrap, origfn) + _updatewrapper(wrap, origfn, wrapper) setattr(container, funcname, wrap) return origfn +def unwrapfunction(container, funcname, wrapper=None): + '''undo wrapfunction + + If wrappers is None, undo the last wrap. Otherwise removes the wrapper + from the chain of wrappers. + + Return the removed wrapper. + Raise IndexError if wrapper is None and nothing to unwrap; ValueError if + wrapper is not None but is not found in the wrapper chain. + ''' + chain = getwrapperchain(container, funcname) + origfn = chain.pop() + if wrapper is None: + wrapper = chain[0] + chain.remove(wrapper) + setattr(container, funcname, origfn) + for w in reversed(chain): + wrapfunction(container, funcname, w) + return wrapper + +def getwrapperchain(container, funcname): + '''get a chain of wrappers of a function + + Return a list of functions: [newest wrapper, ..., oldest wrapper, origfunc] + + The wrapper functions are the ones passed to wrapfunction, whose first + argument is origfunc. + ''' + result = [] + fn = getattr(container, funcname) + while fn: + assert callable(fn) + result.append(getattr(fn, '_unboundwrapper', fn)) + fn = getattr(fn, '_origfunc', None) + return result + def _disabledpaths(strip_init=False): '''find paths of disabled extensions. returns a dict of {name: path} removes /__init__.py from packages if strip_init is True''' @@ -332,6 +380,7 @@ if name in exts or name in _order or name == '__init__': continue exts[name] = path + exts.update(_disabledextensions) return exts def _moduledoc(file): @@ -494,4 +543,4 @@ def ismoduleinternal(module): exttestedwith = getattr(module, 'testedwith', None) - return exttestedwith == "internal" + return exttestedwith == "ships-with-hg-core"
--- a/mercurial/fancyopts.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/fancyopts.py Tue Oct 18 14:15:15 2016 -0500 @@ -12,6 +12,17 @@ from .i18n import _ from . import error +# Set of flags to not apply boolean negation logic on +nevernegate = set([ + # avoid --no-noninteractive + 'noninteractive', + # These two flags are special because they cause hg to do one + # thing and then exit, and so aren't suitable for use in things + # like aliases anyway. + 'help', + 'version', + ]) + def gnugetopt(args, options, longoptions): """Parse options mostly like getopt.gnu_getopt. @@ -64,6 +75,8 @@ shortlist = '' argmap = {} defmap = {} + negations = {} + alllong = set(o[1] for o in options) for option in options: if len(option) == 5: @@ -91,6 +104,18 @@ short += ':' if oname: oname += '=' + elif oname not in nevernegate: + if oname.startswith('no-'): + insert = oname[3:] + else: + insert = 'no-' + oname + # backout (as a practical example) has both --commit and + # --no-commit options, so we don't want to allow the + # negations of those flags. + if insert not in alllong: + assert ('--' + oname) not in negations + negations['--' + insert] = '--' + oname + namelist.append(insert) if short: shortlist += short if name: @@ -105,6 +130,11 @@ # transfer result to state for opt, val in opts: + boolval = True + negation = negations.get(opt, False) + if negation: + opt = negation + boolval = False name = argmap[opt] obj = defmap[name] t = type(obj) @@ -121,7 +151,7 @@ elif t is type([]): state[name].append(val) elif t is type(None) or t is type(False): - state[name] = True + state[name] = boolval # return unparsed args return args
--- a/mercurial/filemerge.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/filemerge.py Tue Oct 18 14:15:15 2016 -0500 @@ -19,6 +19,7 @@ error, formatter, match, + pycompat, scmutil, simplemerge, tagmerge, @@ -93,7 +94,8 @@ '''return a decorator for populating internal merge tool table''' def decorator(func): fullname = ':' + name - func.__doc__ = "``%s``\n" % fullname + func.__doc__.strip() + func.__doc__ = (pycompat.sysstr("``%s``\n" % fullname) + + func.__doc__.strip()) internals[fullname] = func internals['internal:' + name] = func internalsdoc[fullname] = func @@ -230,50 +232,56 @@ util.writefile(file, newdata) @internaltool('prompt', nomerge) -def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf): +def _iprompt(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): """Asks the user which of the local `p1()` or the other `p2()` version to keep as the merged version.""" ui = repo.ui fd = fcd.path() + prompts = partextras(labels) + prompts['fd'] = fd try: if fco.isabsent(): index = ui.promptchoice( - _("local changed %s which remote deleted\n" + _("local%(l)s changed %(fd)s which other%(o)s deleted\n" "use (c)hanged version, (d)elete, or leave (u)nresolved?" - "$$ &Changed $$ &Delete $$ &Unresolved") % fd, 2) + "$$ &Changed $$ &Delete $$ &Unresolved") % prompts, 2) choice = ['local', 'other', 'unresolved'][index] elif fcd.isabsent(): index = ui.promptchoice( - _("remote changed %s which local deleted\n" + _("other%(o)s changed %(fd)s which local%(l)s deleted\n" "use (c)hanged version, leave (d)eleted, or " "leave (u)nresolved?" - "$$ &Changed $$ &Deleted $$ &Unresolved") % fd, 2) + "$$ &Changed $$ &Deleted $$ &Unresolved") % prompts, 2) choice = ['other', 'local', 'unresolved'][index] else: index = ui.promptchoice( - _("no tool found to merge %s\n" - "keep (l)ocal, take (o)ther, or leave (u)nresolved?" - "$$ &Local $$ &Other $$ &Unresolved") % fd, 2) + _("no tool found to merge %(fd)s\n" + "keep (l)ocal%(l)s, take (o)ther%(o)s, or leave (u)nresolved?" + "$$ &Local $$ &Other $$ &Unresolved") % prompts, 2) choice = ['local', 'other', 'unresolved'][index] if choice == 'other': - return _iother(repo, mynode, orig, fcd, fco, fca, toolconf) + return _iother(repo, mynode, orig, fcd, fco, fca, toolconf, + labels) elif choice == 'local': - return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf) + return _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, + labels) elif choice == 'unresolved': - return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf) + return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, + labels) except error.ResponseExpected: ui.write("\n") - return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf) + return _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, + labels) @internaltool('local', nomerge) -def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf): +def _ilocal(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): """Uses the local `p1()` version of files as the merged version.""" return 0, fcd.isabsent() @internaltool('other', nomerge) -def _iother(repo, mynode, orig, fcd, fco, fca, toolconf): +def _iother(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): """Uses the other `p2()` version of files as the merged version.""" if fco.isabsent(): # local changed, remote deleted -- 'deleted' picked @@ -285,7 +293,7 @@ return 0, deleted @internaltool('fail', nomerge) -def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf): +def _ifail(repo, mynode, orig, fcd, fco, fca, toolconf, labels=None): """ Rather than attempting to merge files that were modified on both branches, it marks them as unresolved. The resolve command must be @@ -508,11 +516,11 @@ # 8 for the prefix of conflict marker lines (e.g. '<<<<<<< ') return util.ellipsis(mark, 80 - 8) -_defaultconflictmarker = ('{node|short} ' + - '{ifeq(tags, "tip", "", "{tags} ")}' + - '{if(bookmarks, "{bookmarks} ")}' + - '{ifeq(branch, "default", "", "{branch} ")}' + - '- {author|user}: {desc|firstline}') +_defaultconflictmarker = ('{node|short} ' + '{ifeq(tags, "tip", "", "{tags} ")}' + '{if(bookmarks, "{bookmarks} ")}' + '{ifeq(branch, "default", "", "{branch} ")}' + '- {author|user}: {desc|firstline}') _defaultconflictlabels = ['local', 'other'] @@ -537,6 +545,22 @@ newlabels.append(_formatconflictmarker(repo, ca, tmpl, labels[2], pad)) return newlabels +def partextras(labels): + """Return a dictionary of extra labels for use in prompts to the user + + Intended use is in strings of the form "(l)ocal%(l)s". + """ + if labels is None: + return { + "l": "", + "o": "", + } + + return { + "l": " [%s]" % labels[0], + "o": " [%s]" % labels[1], + } + def _filemerge(premerge, repo, mynode, orig, fcd, fco, fca, labels=None): """perform a 3-way merge in the working directory @@ -588,7 +612,7 @@ toolconf = tool, toolpath, binary, symlink if mergetype == nomerge: - r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf) + r, deleted = func(repo, mynode, orig, fcd, fco, fca, toolconf, labels) return True, r, deleted if premerge:
--- a/mercurial/fileset.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/fileset.py Tue Oct 18 14:15:15 2016 -0500 @@ -345,10 +345,10 @@ def size(mctx, x): """File size matches the given expression. Examples: - - 1k (files from 1024 to 2047 bytes) - - < 20k (files less than 20480 bytes) - - >= .5MB (files at least 524288 bytes) - - 4k - 1MB (files from 4096 bytes to 1048576 bytes) + - size('1k') - files from 1024 to 2047 bytes + - size('< 20k') - files less than 20480 bytes + - size('>= .5MB') - files at least 524288 bytes + - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes """ # i18n: "size" is a keyword
--- a/mercurial/formatter.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/formatter.py Tue Oct 18 14:15:15 2016 -0500 @@ -18,25 +18,45 @@ from . import ( encoding, error, + templatekw, templater, util, ) pickle = util.pickle +class _nullconverter(object): + '''convert non-primitive data types to be processed by formatter''' + @staticmethod + def formatdate(date, fmt): + '''convert date tuple to appropriate format''' + return date + @staticmethod + def formatdict(data, key, value, fmt, sep): + '''convert dict or key-value pairs to appropriate dict format''' + # use plain dict instead of util.sortdict so that data can be + # serialized as a builtin dict in pickle output + return dict(data) + @staticmethod + def formatlist(data, name, fmt, sep): + '''convert iterable to appropriate list format''' + return list(data) + class baseformatter(object): - def __init__(self, ui, topic, opts): + def __init__(self, ui, topic, opts, converter): self._ui = ui self._topic = topic self._style = opts.get("style") self._template = opts.get("template") + self._converter = converter self._item = None # function to convert node to string suitable for this output self.hexfunc = hex - def __nonzero__(self): - '''return False if we're not doing real templating so we can - skip extra work''' - return True + def __enter__(self): + return self + def __exit__(self, exctype, excvalue, traceback): + if exctype is None: + self.end() def _showitem(self): '''show a formatted item once all data is collected''' pass @@ -45,6 +65,17 @@ if self._item is not None: self._showitem() self._item = {} + def formatdate(self, date, fmt='%a %b %d %H:%M:%S %Y %1%2'): + '''convert date tuple to appropriate format''' + return self._converter.formatdate(date, fmt) + def formatdict(self, data, key='key', value='value', fmt='%s=%s', sep=' '): + '''convert dict or key-value pairs to appropriate dict format''' + return self._converter.formatdict(data, key, value, fmt, sep) + def formatlist(self, data, name, fmt='%s', sep=' '): + '''convert iterable to appropriate list format''' + # name is mandatory argument for now, but it could be optional if + # we have default template keyword, e.g. {item} + return self._converter.formatlist(data, name, fmt, sep) def data(self, **data): '''insert data into item that's not shown in default output''' self._item.update(data) @@ -61,21 +92,55 @@ def plain(self, text, **opts): '''show raw text for non-templated mode''' pass + def isplain(self): + '''check for plain formatter usage''' + return False + def nested(self, field): + '''sub formatter to store nested data in the specified field''' + self._item[field] = data = [] + return _nestedformatter(self._ui, self._converter, data) def end(self): '''end output for the formatter''' if self._item is not None: self._showitem() +class _nestedformatter(baseformatter): + '''build sub items and store them in the parent formatter''' + def __init__(self, ui, converter, data): + baseformatter.__init__(self, ui, topic='', opts={}, converter=converter) + self._data = data + def _showitem(self): + self._data.append(self._item) + +def _iteritems(data): + '''iterate key-value pairs in stable order''' + if isinstance(data, dict): + return sorted(data.iteritems()) + return data + +class _plainconverter(object): + '''convert non-primitive data types to text''' + @staticmethod + def formatdate(date, fmt): + '''stringify date tuple in the given format''' + return util.datestr(date, fmt) + @staticmethod + def formatdict(data, key, value, fmt, sep): + '''stringify key-value pairs separated by sep''' + return sep.join(fmt % (k, v) for k, v in _iteritems(data)) + @staticmethod + def formatlist(data, name, fmt, sep): + '''stringify iterable separated by sep''' + return sep.join(fmt % e for e in data) + class plainformatter(baseformatter): '''the default text output scheme''' def __init__(self, ui, topic, opts): - baseformatter.__init__(self, ui, topic, opts) + baseformatter.__init__(self, ui, topic, opts, _plainconverter) if ui.debugflag: self.hexfunc = hex else: self.hexfunc = short - def __nonzero__(self): - return False def startitem(self): pass def data(self, **data): @@ -88,12 +153,17 @@ self._ui.write(deftext % fielddata, **opts) def plain(self, text, **opts): self._ui.write(text, **opts) + def isplain(self): + return True + def nested(self, field): + # nested data will be directly written to ui + return self def end(self): pass class debugformatter(baseformatter): def __init__(self, ui, topic, opts): - baseformatter.__init__(self, ui, topic, opts) + baseformatter.__init__(self, ui, topic, opts, _nullconverter) self._ui.write("%s = [\n" % self._topic) def _showitem(self): self._ui.write(" " + repr(self._item) + ",\n") @@ -103,7 +173,7 @@ class pickleformatter(baseformatter): def __init__(self, ui, topic, opts): - baseformatter.__init__(self, ui, topic, opts) + baseformatter.__init__(self, ui, topic, opts, _nullconverter) self._data = [] def _showitem(self): self._data.append(self._item) @@ -112,7 +182,11 @@ self._ui.write(pickle.dumps(self._data)) def _jsonifyobj(v): - if isinstance(v, tuple): + if isinstance(v, dict): + xs = ['"%s": %s' % (encoding.jsonescape(k), _jsonifyobj(u)) + for k, u in sorted(v.iteritems())] + return '{' + ', '.join(xs) + '}' + elif isinstance(v, (list, tuple)): return '[' + ', '.join(_jsonifyobj(e) for e in v) + ']' elif v is None: return 'null' @@ -127,7 +201,7 @@ class jsonformatter(baseformatter): def __init__(self, ui, topic, opts): - baseformatter.__init__(self, ui, topic, opts) + baseformatter.__init__(self, ui, topic, opts, _nullconverter) self._ui.write("[") self._ui._first = True def _showitem(self): @@ -149,9 +223,32 @@ baseformatter.end(self) self._ui.write("\n]\n") +class _templateconverter(object): + '''convert non-primitive data types to be processed by templater''' + @staticmethod + def formatdate(date, fmt): + '''return date tuple''' + return date + @staticmethod + def formatdict(data, key, value, fmt, sep): + '''build object that can be evaluated as either plain string or dict''' + data = util.sortdict(_iteritems(data)) + def f(): + yield _plainconverter.formatdict(data, key, value, fmt, sep) + return templatekw._hybrid(f(), data, lambda k: {key: k, value: data[k]}, + lambda d: fmt % (d[key], d[value])) + @staticmethod + def formatlist(data, name, fmt, sep): + '''build object that can be evaluated as either plain string or list''' + data = list(data) + def f(): + yield _plainconverter.formatlist(data, name, fmt, sep) + return templatekw._hybrid(f(), data, lambda x: {name: x}, + lambda d: fmt % d[name]) + class templateformatter(baseformatter): def __init__(self, ui, topic, opts): - baseformatter.__init__(self, ui, topic, opts) + baseformatter.__init__(self, ui, topic, opts, _templateconverter) self._topic = topic self._t = gettemplater(ui, topic, opts.get('template', '')) def _showitem(self):
--- a/mercurial/hbisect.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/hbisect.py Tue Oct 18 14:15:15 2016 -0500 @@ -139,6 +139,19 @@ return ([best_node], tot, good) +def extendrange(repo, state, nodes, good): + # bisect is incomplete when it ends on a merge node and + # one of the parent was not checked. + parents = repo[nodes[0]].parents() + if len(parents) > 1: + if good: + side = state['bad'] + else: + side = state['good'] + num = len(set(i.node() for i in parents) & set(side)) + if num == 1: + return parents[0].ancestor(parents[1]) + return None def load_state(repo): state = {'current': [], 'good': [], 'bad': [], 'skip': []} @@ -159,6 +172,22 @@ f.write("%s %s\n" % (kind, hex(node))) f.close() +def resetstate(repo): + """remove any bisect state from the repository""" + if repo.vfs.exists("bisect.state"): + repo.vfs.unlink("bisect.state") + +def checkstate(state): + """check we have both 'good' and 'bad' to define a range + + Raise Abort exception otherwise.""" + if state['good'] and state['bad']: + return True + if not state['good']: + raise error.Abort(_('cannot bisect (no known good revisions)')) + else: + raise error.Abort(_('cannot bisect (no known bad revisions)')) + def get(repo, status): """ Return a list of revision(s) that match the given status: @@ -261,3 +290,29 @@ return label[0].upper() return None + +def printresult(ui, repo, state, displayer, nodes, good): + if len(nodes) == 1: + # narrowed it down to a single revision + if good: + ui.write(_("The first good revision is:\n")) + else: + ui.write(_("The first bad revision is:\n")) + displayer.show(repo[nodes[0]]) + extendnode = extendrange(repo, state, nodes, good) + if extendnode is not None: + ui.write(_('Not all ancestors of this changeset have been' + ' checked.\nUse bisect --extend to continue the ' + 'bisection from\nthe common ancestor, %s.\n') + % extendnode) + else: + # multiple possible revisions + if good: + ui.write(_("Due to skipped revisions, the first " + "good revision could be any of:\n")) + else: + ui.write(_("Due to skipped revisions, the first " + "bad revision could be any of:\n")) + for n in nodes: + displayer.show(repo[n]) + displayer.close()
--- a/mercurial/help.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/help.py Tue Oct 18 14:15:15 2016 -0500 @@ -184,14 +184,16 @@ return loader internalstable = sorted([ - (['bundles'], _('container for exchange of repository data'), + (['bundles'], _('Bundles'), loaddoc('bundles', subdir='internals')), - (['changegroups'], _('representation of revlog data'), + (['changegroups'], _('Changegroups'), loaddoc('changegroups', subdir='internals')), - (['requirements'], _('repository requirements'), + (['requirements'], _('Repository Requirements'), loaddoc('requirements', subdir='internals')), - (['revlogs'], _('revision storage mechanism'), + (['revlogs'], _('Revision Logs'), loaddoc('revlogs', subdir='internals')), + (['wireprotocol'], _('Wire Protocol'), + loaddoc('wireprotocol', subdir='internals')), ]) def internalshelp(ui): @@ -356,8 +358,8 @@ mod = extensions.find(name) doc = gettext(mod.__doc__) or '' if '\n' in doc.strip(): - msg = _('(use "hg help -e %s" to show help for ' - 'the %s extension)') % (name, name) + msg = _("(use 'hg help -e %s' to show help for " + "the %s extension)") % (name, name) rst.append('\n%s\n' % msg) except KeyError: pass @@ -372,7 +374,7 @@ if not ui.verbose: if not full: - rst.append(_('\n(use "hg %s -h" to show more help)\n') + rst.append(_("\n(use 'hg %s -h' to show more help)\n") % name) elif not ui.quiet: rst.append(_('\n(some details hidden, use --verbose ' @@ -448,21 +450,21 @@ rst.append('\n%s\n' % optrst(_("global options"), commands.globalopts, ui.verbose)) if name == 'shortlist': - rst.append(_('\n(use "hg help" for the full list ' - 'of commands)\n')) + rst.append(_("\n(use 'hg help' for the full list " + "of commands)\n")) else: if name == 'shortlist': - rst.append(_('\n(use "hg help" for the full list of commands ' - 'or "hg -v" for details)\n')) + rst.append(_("\n(use 'hg help' for the full list of commands " + "or 'hg -v' for details)\n")) elif name and not full: - rst.append(_('\n(use "hg help %s" to show the full help ' - 'text)\n') % name) + rst.append(_("\n(use 'hg help %s' to show the full help " + "text)\n") % name) elif name and cmds and name in cmds.keys(): - rst.append(_('\n(use "hg help -v -e %s" to show built-in ' - 'aliases and global options)\n') % name) + rst.append(_("\n(use 'hg help -v -e %s' to show built-in " + "aliases and global options)\n") % name) else: - rst.append(_('\n(use "hg help -v%s" to show built-in aliases ' - 'and global options)\n') + rst.append(_("\n(use 'hg help -v%s' to show built-in aliases " + "and global options)\n") % (name and " " + name or "")) return rst @@ -496,8 +498,8 @@ try: cmdutil.findcmd(name, commands.table) - rst.append(_('\nuse "hg help -c %s" to see help for ' - 'the %s command\n') % (name, name)) + rst.append(_("\nuse 'hg help -c %s' to see help for " + "the %s command\n") % (name, name)) except error.UnknownCommand: pass return rst @@ -534,8 +536,8 @@ modcmds = set([c.partition('|')[0] for c in ct]) rst.extend(helplist(modcmds.__contains__)) else: - rst.append(_('(use "hg help extensions" for information on enabling' - ' extensions)\n')) + rst.append(_("(use 'hg help extensions' for information on enabling" + " extensions)\n")) return rst def helpextcmd(name, subtopic=None): @@ -547,8 +549,8 @@ "extension:") % cmd, {ext: doc}, indent=4, showdeprecated=True) rst.append('\n') - rst.append(_('(use "hg help extensions" for information on enabling ' - 'extensions)\n')) + rst.append(_("(use 'hg help extensions' for information on enabling " + "extensions)\n")) return rst @@ -573,7 +575,7 @@ rst.append('\n') if not rst: msg = _('no matches') - hint = _('try "hg help" for a list of topics') + hint = _("try 'hg help' for a list of topics") raise error.Abort(msg, hint=hint) elif name and name != 'shortlist': queries = [] @@ -596,7 +598,7 @@ raise error.UnknownCommand(name) else: msg = _('no such help topic: %s') % name - hint = _('try "hg help --keyword %s"') % name + hint = _("try 'hg help --keyword %s'") % name raise error.Abort(msg, hint=hint) else: # program name
--- a/mercurial/help/config.txt Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/help/config.txt Tue Oct 18 14:15:15 2016 -0500 @@ -1393,6 +1393,12 @@ statistical text report generated from the profiling data. The profiling is done using lsprof. +``enabled`` + Enable the profiler. + (default: false) + + This is equivalent to passing ``--profile`` on the command line. + ``type`` The type of profiler to use. (default: ls) @@ -1557,6 +1563,21 @@ repositories to the exchange format required by the bundle1 data format can consume a lot of CPU. +``zliblevel`` + Integer between ``-1`` and ``9`` that controls the zlib compression level + for wire protocol commands that send zlib compressed output (notably the + commands that send repository history data). + + The default (``-1``) uses the default zlib compression level, which is + likely equivalent to ``6``. ``0`` means no compression. ``9`` means + maximum compression. + + Setting this option allows server operators to make trade-offs between + bandwidth and CPU used. Lowering the compression lowers CPU utilization + but sends more bytes to clients. + + This option only impacts the HTTP server. + ``smtp`` --------
--- a/mercurial/help/internals/bundles.txt Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/help/internals/bundles.txt Tue Oct 18 14:15:15 2016 -0500 @@ -1,6 +1,3 @@ -Bundles -======= - A bundle is a container for repository data. Bundles are used as standalone files as well as the interchange format @@ -8,7 +5,7 @@ each other. Headers -------- +======= Bundles produced since Mercurial 0.7 (September 2005) have a 4 byte header identifying the major bundle type. The header always begins with
--- a/mercurial/help/internals/changegroups.txt Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/help/internals/changegroups.txt Tue Oct 18 14:15:15 2016 -0500 @@ -1,6 +1,3 @@ -Changegroups -============ - Changegroups are representations of repository revlog data, specifically the changelog, manifest, and filelogs. @@ -35,7 +32,7 @@ call this an *empty chunk*. Delta Groups ------------- +============ A *delta group* expresses the content of a revlog as a series of deltas, or patches against previous revisions. @@ -111,21 +108,21 @@ which can result in smaller deltas and more efficient encoding of data. Changeset Segment ------------------ +================= The *changeset segment* consists of a single *delta group* holding changelog data. It is followed by an *empty chunk* to denote the boundary to the *manifests segment*. Manifest Segment ----------------- +================ The *manifest segment* consists of a single *delta group* holding manifest data. It is followed by an *empty chunk* to denote the boundary to the *filelogs segment*. Filelogs Segment ----------------- +================ The *filelogs* segment consists of multiple sub-segments, each corresponding to an individual file whose data is being described:: @@ -154,4 +151,3 @@ That is, a *chunk* consisting of the filename (not terminated or padded) followed by N chunks constituting the *delta group* for this file. -
--- a/mercurial/help/internals/requirements.txt Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/help/internals/requirements.txt Tue Oct 18 14:15:15 2016 -0500 @@ -1,5 +1,3 @@ -Requirements -============ Repositories contain a file (``.hg/requires``) containing a list of features/capabilities that are *required* for clients to interface @@ -19,7 +17,7 @@ Mercurial core distribution. revlogv1 --------- +======== When present, revlogs are version 1 (RevlogNG). RevlogNG was introduced in 2006. The ``revlogv1`` requirement has been enabled by default @@ -28,7 +26,7 @@ If this requirement is not present, version 0 revlogs are assumed. store ------ +===== The *store* repository layout should be used. @@ -36,7 +34,7 @@ was introduced in Mercurial 0.9.2. fncache -------- +======= The *fncache* repository layout should be used. @@ -48,7 +46,7 @@ 1.1 (released December 2008). shared ------- +====== Denotes that the store for a repository is shared from another location (defined by the ``.hg/sharedpath`` file). @@ -58,7 +56,7 @@ The requirement was added in Mercurial 1.3 (released July 2009). dotencode ---------- +========= The *dotencode* repository layout should be used. @@ -70,7 +68,7 @@ Mercurial 1.7 (released November 2010). parentdelta ------------ +=========== Denotes a revlog delta encoding format that was experimental and replaced by *generaldelta*. It should not be seen in the wild because @@ -80,7 +78,7 @@ 1.9. generaldelta ------------- +============ Revlogs should be created with the *generaldelta* flag enabled. The generaldelta flag will cause deltas to be encoded against a parent @@ -91,7 +89,7 @@ default until Mercurial 3.7 (released February 2016). manifestv2 ----------- +========== Denotes that version 2 of manifests are being used. @@ -100,7 +98,7 @@ by default. treemanifest ------------- +============ Denotes that tree manifests are being used. Tree manifests are one manifest per directory (as opposed to a single flat manifest).
--- a/mercurial/help/internals/revlogs.txt Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/help/internals/revlogs.txt Tue Oct 18 14:15:15 2016 -0500 @@ -1,6 +1,3 @@ -Revlogs -======= - Revision logs - or *revlogs* - are an append only data structure for storing discrete entries, or *revisions*. They are the primary storage mechanism of repository data. @@ -28,7 +25,7 @@ used to mean *does not exist* or *not defined*. File Format ------------ +=========== A revlog begins with a 32-bit big endian integer holding version info and feature flags. This integer is shared with the first revision @@ -77,7 +74,7 @@ below. RevlogNG Format ---------------- +=============== RevlogNG (version 1) begins with an index describing the revisions in the revlog. If the ``inline`` flag is set, revision data is stored inline, @@ -129,7 +126,7 @@ and the 6 byte absolute offset field from the first revlog entry. Delta Chains ------------- +============ Revision data is encoded as a chain of *chunks*. Each chain begins with the compressed original full text for that revision. Each subsequent @@ -153,7 +150,7 @@ computed against an arbitrary revision (almost certainly a parent revision). File Storage ------------- +============ Revlogs logically consist of an index (metadata of entries) and revision data. This data may be stored together in a single file or in @@ -172,7 +169,7 @@ (possibly containing inline data) and a ``.d`` file holds the revision data. Revision Entries ----------------- +================ Revision entries consist of an optional 1 byte header followed by an encoding of the revision data. The headers are as follows: @@ -187,7 +184,7 @@ The 0x78 value is actually the first byte of the zlib header (CMF byte). Hash Computation ----------------- +================ The hash of the revision is stored in the index and is used both as a primary key and for data integrity verification.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/help/internals/wireprotocol.txt Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,773 @@ +The Mercurial wire protocol is a request-response based protocol +with multiple wire representations. + +Each request is modeled as a command name, a dictionary of arguments, and +optional raw input. Command arguments and their types are intrinsic +properties of commands. So is the response type of the command. This means +clients can't always send arbitrary arguments to servers and servers can't +return multiple response types. + +The protocol is synchronous and does not support multiplexing (concurrent +commands). + +Transport Protocols +=================== + +HTTP Transport +-------------- + +Commands are issued as HTTP/1.0 or HTTP/1.1 requests. Commands are +sent to the base URL of the repository with the command name sent in +the ``cmd`` query string parameter. e.g. +``https://example.com/repo?cmd=capabilities``. The HTTP method is ``GET`` +or ``POST`` depending on the command and whether there is a request +body. + +Command arguments can be sent multiple ways. + +The simplest is part of the URL query string using ``x-www-form-urlencoded`` +encoding (see Python's ``urllib.urlencode()``. However, many servers impose +length limitations on the URL. So this mechanism is typically only used if +the server doesn't support other mechanisms. + +If the server supports the ``httpheader`` capability, command arguments can +be sent in HTTP request headers named ``X-HgArg-<N>`` where ``<N>`` is an +integer starting at 1. A ``x-www-form-urlencoded`` representation of the +arguments is obtained. This full string is then split into chunks and sent +in numbered ``X-HgArg-<N>`` headers. The maximum length of each HTTP header +is defined by the server in the ``httpheader`` capability value, which defaults +to ``1024``. The server reassembles the encoded arguments string by +concatenating the ``X-HgArg-<N>`` headers then URL decodes them into a +dictionary. + +The list of ``X-HgArg-<N>`` headers should be added to the ``Vary`` request +header to instruct caches to take these headers into consideration when caching +requests. + +If the server supports the ``httppostargs`` capability, the client +may send command arguments in the HTTP request body as part of an +HTTP POST request. The command arguments will be URL encoded just like +they would for sending them via HTTP headers. However, no splitting is +performed: the raw arguments are included in the HTTP request body. + +The client sends a ``X-HgArgs-Post`` header with the string length of the +encoded arguments data. Additional data may be included in the HTTP +request body immediately following the argument data. The offset of the +non-argument data is defined by the ``X-HgArgs-Post`` header. The +``X-HgArgs-Post`` header is not required if there is no argument data. + +Additional command data can be sent as part of the HTTP request body. The +default ``Content-Type`` when sending data is ``application/mercurial-0.1``. +A ``Content-Length`` header is currently always sent. + +Example HTTP requests:: + + GET /repo?cmd=capabilities + X-HgArg-1: foo=bar&baz=hello%20world + +The ``Content-Type`` HTTP response header identifies the response as coming +from Mercurial and can also be used to signal an error has occurred. + +The ``application/mercurial-0.1`` media type indicates a generic Mercurial +response. It matches the media type sent by the client. + +The ``application/hg-error`` media type indicates a generic error occurred. +The content of the HTTP response body typically holds text describing the +error. + +The ``application/hg-changegroup`` media type indicates a changegroup response +type. + +Clients also accept the ``text/plain`` media type. All other media +types should cause the client to error. + +Clients should issue a ``User-Agent`` request header that identifies the client. +The server should not use the ``User-Agent`` for feature detection. + +A command returning a ``string`` response issues the +``application/mercurial-0.1`` media type and the HTTP response body contains +the raw string value. A ``Content-Length`` header is typically issued. + +A command returning a ``stream`` response issues the +``application/mercurial-0.1`` media type and the HTTP response is typically +using *chunked transfer* (``Transfer-Encoding: chunked``). + +SSH Transport +============= + +The SSH transport is a custom text-based protocol suitable for use over any +bi-directional stream transport. It is most commonly used with SSH. + +A SSH transport server can be started with ``hg serve --stdio``. The stdin, +stderr, and stdout file descriptors of the started process are used to exchange +data. When Mercurial connects to a remote server over SSH, it actually starts +a ``hg serve --stdio`` process on the remote server. + +Commands are issued by sending the command name followed by a trailing newline +``\n`` to the server. e.g. ``capabilities\n``. + +Command arguments are sent in the following format:: + + <argument> <length>\n<value> + +That is, the argument string name followed by a space followed by the +integer length of the value (expressed as a string) followed by a newline +(``\n``) followed by the raw argument value. + +Dictionary arguments are encoded differently:: + + <argument> <# elements>\n + <key1> <length1>\n<value1> + <key2> <length2>\n<value2> + ... + +Non-argument data is sent immediately after the final argument value. It is +encoded in chunks:: + + <length>\n<data> + +Each command declares a list of supported arguments and their types. If a +client sends an unknown argument to the server, the server should abort +immediately. The special argument ``*`` in a command's definition indicates +that all argument names are allowed. + +The definition of supported arguments and types is initially made when a +new command is implemented. The client and server must initially independently +agree on the arguments and their types. This initial set of arguments can be +supplemented through the presence of *capabilities* advertised by the server. + +Each command has a defined expected response type. + +A ``string`` response type is a length framed value. The response consists of +the string encoded integer length of a value followed by a newline (``\n``) +followed by the value. Empty values are allowed (and are represented as +``0\n``). + +A ``stream`` response type consists of raw bytes of data. There is no framing. + +A generic error response type is also supported. It consists of a an error +message written to ``stderr`` followed by ``\n-\n``. In addition, ``\n`` is +written to ``stdout``. + +If the server receives an unknown command, it will send an empty ``string`` +response. + +The server terminates if it receives an empty command (a ``\n`` character). + +Capabilities +============ + +Servers advertise supported wire protocol features. This allows clients to +probe for server features before blindly calling a command or passing a +specific argument. + +The server's features are exposed via a *capabilities* string. This is a +space-delimited string of tokens/features. Some features are single words +like ``lookup`` or ``batch``. Others are complicated key-value pairs +advertising sub-features. e.g. ``httpheader=2048``. When complex, non-word +values are used, each feature name can define its own encoding of sub-values. +Comma-delimited and ``x-www-form-urlencoded`` values are common. + +The following document capabilities defined by the canonical Mercurial server +implementation. + +batch +----- + +Whether the server supports the ``batch`` command. + +This capability/command was introduced in Mercurial 1.9 (released July 2011). + +branchmap +--------- + +Whether the server supports the ``branchmap`` command. + +This capability/command was introduced in Mercurial 1.3 (released July 2009). + +bundle2-exp +----------- + +Precursor to ``bundle2`` capability that was used before bundle2 was a +stable feature. + +This capability was introduced in Mercurial 3.0 behind an experimental +flag. This capability should not be observed in the wild. + +bundle2 +------- + +Indicates whether the server supports the ``bundle2`` data exchange format. + +The value of the capability is a URL quoted, newline (``\n``) delimited +list of keys or key-value pairs. + +A key is simply a URL encoded string. + +A key-value pair is a URL encoded key separated from a URL encoded value by +an ``=``. If the value is a list, elements are delimited by a ``,`` after +URL encoding. + +For example, say we have the values:: + + {'HG20': [], 'changegroup': ['01', '02'], 'digests': ['sha1', 'sha512']} + +We would first construct a string:: + + HG20\nchangegroup=01,02\ndigests=sha1,sha512 + +We would then URL quote this string:: + + HG20%0Achangegroup%3D01%2C02%0Adigests%3Dsha1%2Csha512 + +This capability was introduced in Mercurial 3.4 (released May 2015). + +changegroupsubset +----------------- + +Whether the server supports the ``changegroupsubset`` command. + +This capability was introduced in Mercurial 0.9.2 (released December +2006). + +This capability was introduced at the same time as the ``lookup`` +capability/command. + +getbundle +--------- + +Whether the server supports the ``getbundle`` command. + +This capability was introduced in Mercurial 1.9 (released July 2011). + +httpheader +---------- + +Whether the server supports receiving command arguments via HTTP request +headers. + +The value of the capability is an integer describing the max header +length that clients should send. Clients should ignore any content after a +comma in the value, as this is reserved for future use. + +This capability was introduced in Mercurial 1.9 (released July 2011). + +httppostargs +------------ + +**Experimental** + +Indicates that the server supports and prefers clients send command arguments +via a HTTP POST request as part of the request body. + +This capability was introduced in Mercurial 3.8 (released May 2016). + +known +----- + +Whether the server supports the ``known`` command. + +This capability/command was introduced in Mercurial 1.9 (released July 2011). + +lookup +------ + +Whether the server supports the ``lookup`` command. + +This capability was introduced in Mercurial 0.9.2 (released December +2006). + +This capability was introduced at the same time as the ``changegroupsubset`` +capability/command. + +pushkey +------- + +Whether the server supports the ``pushkey`` and ``listkeys`` commands. + +This capability was introduced in Mercurial 1.6 (released July 2010). + +standardbundle +-------------- + +**Unsupported** + +This capability was introduced during the Mercurial 0.9.2 development cycle in +2006. It was never present in a release, as it was replaced by the ``unbundle`` +capability. This capability should not be encountered in the wild. + +stream-preferred +---------------- + +If present the server prefers that clients clone using the streaming clone +protocol (``hg clone --uncompressed``) rather than the standard +changegroup/bundle based protocol. + +This capability was introduced in Mercurial 2.2 (released May 2012). + +streamreqs +---------- + +Indicates whether the server supports *streaming clones* and the *requirements* +that clients must support to receive it. + +If present, the server supports the ``stream_out`` command, which transmits +raw revlogs from the repository instead of changegroups. This provides a faster +cloning mechanism at the expense of more bandwidth used. + +The value of this capability is a comma-delimited list of repo format +*requirements*. These are requirements that impact the reading of data in +the ``.hg/store`` directory. An example value is +``streamreqs=generaldelta,revlogv1`` indicating the server repo requires +the ``revlogv1`` and ``generaldelta`` requirements. + +If the only format requirement is ``revlogv1``, the server may expose the +``stream`` capability instead of the ``streamreqs`` capability. + +This capability was introduced in Mercurial 1.7 (released November 2010). + +stream +------ + +Whether the server supports *streaming clones* from ``revlogv1`` repos. + +If present, the server supports the ``stream_out`` command, which transmits +raw revlogs from the repository instead of changegroups. This provides a faster +cloning mechanism at the expense of more bandwidth used. + +This capability was introduced in Mercurial 0.9.1 (released July 2006). + +When initially introduced, the value of the capability was the numeric +revlog revision. e.g. ``stream=1``. This indicates the changegroup is using +``revlogv1``. This simple integer value wasn't powerful enough, so the +``streamreqs`` capability was invented to handle cases where the repo +requirements have more than just ``revlogv1``. Newer servers omit the +``=1`` since it was the only value supported and the value of ``1`` can +be implied by clients. + +unbundlehash +------------ + +Whether the ``unbundle`` commands supports receiving a hash of all the +heads instead of a list. + +For more, see the documentation for the ``unbundle`` command. + +This capability was introduced in Mercurial 1.9 (released July 2011). + +unbundle +-------- + +Whether the server supports pushing via the ``unbundle`` command. + +This capability/command has been present since Mercurial 0.9.1 (released +July 2006). + +Mercurial 0.9.2 (released December 2006) added values to the capability +indicating which bundle types the server supports receiving. This value is a +comma-delimited list. e.g. ``HG10GZ,HG10BZ,HG10UN``. The order of values +reflects the priority/preference of that type, where the first value is the +most preferred type. + +Handshake Protocol +================== + +While not explicitly required, it is common for clients to perform a +*handshake* when connecting to a server. The handshake accomplishes 2 things: + +* Obtaining capabilities and other server features +* Flushing extra server output (e.g. SSH servers may print extra text + when connecting that may confuse the wire protocol) + +This isn't a traditional *handshake* as far as network protocols go because +there is no persistent state as a result of the handshake: the handshake is +simply the issuing of commands and commands are stateless. + +The canonical clients perform a capabilities lookup at connection establishment +time. This is because clients must assume a server only supports the features +of the original Mercurial server implementation until proven otherwise (from +advertised capabilities). Nearly every server running today supports features +that weren't present in the original Mercurial server implementation. Rather +than wait for a client to perform functionality that needs to consult +capabilities, it issues the lookup at connection start to avoid any delay later. + +For HTTP servers, the client sends a ``capabilities`` command request as +soon as the connection is established. The server responds with a capabilities +string, which the client parses. + +For SSH servers, the client sends the ``hello`` command (no arguments) +and a ``between`` command with the ``pairs`` argument having the value +``0000000000000000000000000000000000000000-0000000000000000000000000000000000000000``. + +The ``between`` command has been supported since the original Mercurial +server. Requesting the empty range will return a ``\n`` string response, +which will be encoded as ``1\n\n`` (value length of ``1`` followed by a newline +followed by the value, which happens to be a newline). + +The ``hello`` command was later introduced. Servers supporting it will issue +a response to that command before sending the ``1\n\n`` response to the +``between`` command. Servers not supporting ``hello`` will send an empty +response (``0\n``). + +In addition to the expected output from the ``hello`` and ``between`` commands, +servers may also send other output, such as *message of the day (MOTD)* +announcements. Clients assume servers will send this output before the +Mercurial server replies to the client-issued commands. So any server output +not conforming to the expected command responses is assumed to be not related +to Mercurial and can be ignored. + +Commands +======== + +This section contains a list of all wire protocol commands implemented by +the canonical Mercurial server. + +batch +----- + +Issue multiple commands while sending a single command request. The purpose +of this command is to allow a client to issue multiple commands while avoiding +multiple round trips to the server therefore enabling commands to complete +quicker. + +The command accepts a ``cmds`` argument that contains a list of commands to +execute. + +The value of ``cmds`` is a ``;`` delimited list of strings. Each string has the +form ``<command> <arguments>``. That is, the command name followed by a space +followed by an argument string. + +The argument string is a ``,`` delimited list of ``<key>=<value>`` values +corresponding to command arguments. Both the argument name and value are +escaped using a special substitution map:: + + : -> :c + , -> :o + ; -> :s + = -> :e + +The response type for this command is ``string``. The value contains a +``;`` delimited list of responses for each requested command. Each value +in this list is escaped using the same substitution map used for arguments. + +If an error occurs, the generic error response may be sent. + +between +------- + +(Legacy command used for discovery in old clients) + +Obtain nodes between pairs of nodes. + +The ``pairs`` arguments contains a space-delimited list of ``-`` delimited +hex node pairs. e.g.:: + + a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896-6dc58916e7c070f678682bfe404d2e2d68291a18 + +Return type is a ``string``. Value consists of lines corresponding to each +requested range. Each line contains a space-delimited list of hex nodes. +A newline ``\n`` terminates each line, including the last one. + +branchmap +--------- + +Obtain heads in named branches. + +Accepts no arguments. Return type is a ``string``. + +Return value contains lines with URL encoded branch names followed by a space +followed by a space-delimited list of hex nodes of heads on that branch. +e.g.:: + + default a072279d3f7fd3a4aa7ffa1a5af8efc573e1c896 6dc58916e7c070f678682bfe404d2e2d68291a18 + stable baae3bf31522f41dd5e6d7377d0edd8d1cf3fccc + +There is no trailing newline. + +branches +-------- + +Obtain ancestor changesets of specific nodes back to a branch point. + +Despite the name, this command has nothing to do with Mercurial named branches. +Instead, it is related to DAG branches. + +The command accepts a ``nodes`` argument, which is a string of space-delimited +hex nodes. + +For each node requested, the server will find the first ancestor node that is +a DAG root or is a merge. + +Return type is a ``string``. Return value contains lines with result data for +each requested node. Each line contains space-delimited nodes followed by a +newline (``\n``). The 4 nodes reported on each line correspond to the requested +node, the ancestor node found, and its 2 parent nodes (which may be the null +node). + +capabilities +------------ + +Obtain the capabilities string for the repo. + +Unlike the ``hello`` command, the capabilities string is not prefixed. +There is no trailing newline. + +This command does not accept any arguments. Return type is a ``string``. + +changegroup +----------- + +(Legacy command: use ``getbundle`` instead) + +Obtain a changegroup version 1 with data for changesets that are +descendants of client-specified changesets. + +The ``roots`` arguments contains a list of space-delimited hex nodes. + +The server responds with a changegroup version 1 containing all +changesets between the requested root/base nodes and the repo's head nodes +at the time of the request. + +The return type is a ``stream``. + +changegroupsubset +----------------- + +(Legacy command: use ``getbundle`` instead) + +Obtain a changegroup version 1 with data for changesetsets between +client specified base and head nodes. + +The ``bases`` argument contains a list of space-delimited hex nodes. +The ``heads`` argument contains a list of space-delimited hex nodes. + +The server responds with a changegroup version 1 containing all +changesets between the requested base and head nodes at the time of the +request. + +The return type is a ``stream``. + +clonebundles +------------ + +Obtains a manifest of bundle URLs available to seed clones. + +Each returned line contains a URL followed by metadata. See the +documentation in the ``clonebundles`` extension for more. + +The return type is a ``string``. + +getbundle +--------- + +Obtain a bundle containing repository data. + +This command accepts the following arguments: + +heads + List of space-delimited hex nodes of heads to retrieve. +common + List of space-delimited hex nodes that the client has in common with the + server. +obsmarkers + Boolean indicating whether to include obsolescence markers as part + of the response. Only works with bundle2. +bundlecaps + Comma-delimited set of strings defining client bundle capabilities. +listkeys + Comma-delimited list of strings of ``pushkey`` namespaces. For each + namespace listed, a bundle2 part will be included with the content of + that namespace. +cg + Boolean indicating whether changegroup data is requested. +cbattempted + Boolean indicating whether the client attempted to use the *clone bundles* + feature before performing this request. + +The return type on success is a ``stream`` where the value is bundle. +On the HTTP transport, the response is zlib compressed. + +If an error occurs, a generic error response can be sent. + +Unless the client sends a false value for the ``cg`` argument, the returned +bundle contains a changegroup with the nodes between the specified ``common`` +and ``heads`` nodes. Depending on the command arguments, the type and content +of the returned bundle can vary significantly. + +The default behavior is for the server to send a raw changegroup version +``01`` response. + +If the ``bundlecaps`` provided by the client contain a value beginning +with ``HG2``, a bundle2 will be returned. The bundle2 data may contain +additional repository data, such as ``pushkey`` namespace values. + +heads +----- + +Returns a list of space-delimited hex nodes of repository heads followed +by a newline. e.g. +``a9eeb3adc7ddb5006c088e9eda61791c777cbf7c 31f91a3da534dc849f0d6bfc00a395a97cf218a1\n`` + +This command does not accept any arguments. The return type is a ``string``. + +hello +----- + +Returns lines describing interesting things about the server in an RFC-822 +like format. + +Currently, the only line defines the server capabilities. It has the form:: + + capabilities: <value> + +See above for more about the capabilities string. + +SSH clients typically issue this command as soon as a connection is +established. + +This command does not accept any arguments. The return type is a ``string``. + +listkeys +-------- + +List values in a specified ``pushkey`` namespace. + +The ``namespace`` argument defines the pushkey namespace to operate on. + +The return type is a ``string``. The value is an encoded dictionary of keys. + +Key-value pairs are delimited by newlines (``\n``). Within each line, keys and +values are separated by a tab (``\t``). Keys and values are both strings. + +lookup +------ + +Try to resolve a value to a known repository revision. + +The ``key`` argument is converted from bytes to an +``encoding.localstr`` instance then passed into +``localrepository.__getitem__`` in an attempt to resolve it. + +The return type is a ``string``. + +Upon successful resolution, returns ``1 <hex node>\n``. On failure, +returns ``0 <error string>\n``. e.g.:: + + 1 273ce12ad8f155317b2c078ec75a4eba507f1fba\n + + 0 unknown revision 'foo'\n + +known +----- + +Determine whether multiple nodes are known. + +The ``nodes`` argument is a list of space-delimited hex nodes to check +for existence. + +The return type is ``string``. + +Returns a string consisting of ``0``s and ``1``s indicating whether nodes +are known. If the Nth node specified in the ``nodes`` argument is known, +a ``1`` will be returned at byte offset N. If the node isn't known, ``0`` +will be present at byte offset N. + +There is no trailing newline. + +pushkey +------- + +Set a value using the ``pushkey`` protocol. + +Accepts arguments ``namespace``, ``key``, ``old``, and ``new``, which +correspond to the pushkey namespace to operate on, the key within that +namespace to change, the old value (which may be empty), and the new value. +All arguments are string types. + +The return type is a ``string``. The value depends on the transport protocol. + +The SSH transport sends a string encoded integer followed by a newline +(``\n``) which indicates operation result. The server may send additional +output on the ``stderr`` stream that should be displayed to the user. + +The HTTP transport sends a string encoded integer followed by a newline +followed by additional server output that should be displayed to the user. +This may include output from hooks, etc. + +The integer result varies by namespace. ``0`` means an error has occurred +and there should be additional output to display to the user. + +stream_out +---------- + +Obtain *streaming clone* data. + +The return type is either a ``string`` or a ``stream``, depending on +whether the request was fulfilled properly. + +A return value of ``1\n`` indicates the server is not configured to serve +this data. If this is seen by the client, they may not have verified the +``stream`` capability is set before making the request. + +A return value of ``2\n`` indicates the server was unable to lock the +repository to generate data. + +All other responses are a ``stream`` of bytes. The first line of this data +contains 2 space-delimited integers corresponding to the path count and +payload size, respectively:: + + <path count> <payload size>\n + +The ``<payload size>`` is the total size of path data: it does not include +the size of the per-path header lines. + +Following that header are ``<path count>`` entries. Each entry consists of a +line with metadata followed by raw revlog data. The line consists of:: + + <store path>\0<size>\n + +The ``<store path>`` is the encoded store path of the data that follows. +``<size>`` is the amount of data for this store path/revlog that follows the +newline. + +There is no trailer to indicate end of data. Instead, the client should stop +reading after ``<path count>`` entries are consumed. + +unbundle +-------- + +Send a bundle containing data (usually changegroup data) to the server. + +Accepts the argument ``heads``, which is a space-delimited list of hex nodes +corresponding to server repository heads observed by the client. This is used +to detect race conditions and abort push operations before a server performs +too much work or a client transfers too much data. + +The request payload consists of a bundle to be applied to the repository, +similarly to as if :hg:`unbundle` were called. + +In most scenarios, a special ``push response`` type is returned. This type +contains an integer describing the change in heads as a result of the +operation. A value of ``0`` indicates nothing changed. ``1`` means the number +of heads remained the same. Values ``2`` and larger indicate the number of +added heads minus 1. e.g. ``3`` means 2 heads were added. Negative values +indicate the number of fewer heads, also off by 1. e.g. ``-2`` means there +is 1 fewer head. + +The encoding of the ``push response`` type varies by transport. + +For the SSH transport, this type is composed of 2 ``string`` responses: an +empty response (``0\n``) followed by the integer result value. e.g. +``1\n2``. So the full response might be ``0\n1\n2``. + +For the HTTP transport, the response is a ``string`` type composed of an +integer result value followed by a newline (``\n``) followed by string +content holding server output that should be displayed on the client (output +hooks, etc). + +In some cases, the server may respond with a ``bundle2`` bundle. In this +case, the response type is ``stream``. For the HTTP transport, the response +is zlib compressed. + +The server may also respond with a generic error type, which contains a string +indicating the failure.
--- a/mercurial/help/revsets.txt Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/help/revsets.txt Tue Oct 18 14:15:15 2016 -0500 @@ -12,11 +12,17 @@ e.g., ``\n`` is interpreted as a newline. To prevent them from being interpreted, strings can be prefixed with ``r``, e.g. ``r'...'``. +Prefix +====== + There is a single prefix operator: ``not x`` Changesets not in x. Short form is ``! x``. +Infix +===== + These are the supported infix operators: ``x::y`` @@ -55,16 +61,40 @@ ``x~n`` The nth first ancestor of x; ``x~0`` is x; ``x~3`` is ``x^^^``. +``x ## y`` + Concatenate strings and identifiers into one string. + + All other prefix, infix and postfix operators have lower priority than + ``##``. For example, ``a1 ## a2~2`` is equivalent to ``(a1 ## a2)~2``. + + For example:: + + [revsetalias] + issue(a1) = grep(r'\bissue[ :]?' ## a1 ## r'\b|\bbug\(' ## a1 ## r'\)') + + ``issue(1234)`` is equivalent to + ``grep(r'\bissue[ :]?1234\b|\bbug\(1234\)')`` + in this case. This matches against all of "issue 1234", "issue:1234", + "issue1234" and "bug(1234)". + +Postfix +======= + There is a single postfix operator: ``x^`` Equivalent to ``x^1``, the first parent of each changeset in x. +Predicates +========== The following predicates are supported: .. predicatesmarker +Aliases +======= + New predicates (known as "aliases") can be defined, using any combination of existing predicates or other aliases. An alias definition looks like:: @@ -86,18 +116,8 @@ defines three aliases, ``h``, ``d``, and ``rs``. ``rs(0:tip, author)`` is exactly equivalent to ``reverse(sort(0:tip, author))``. -An infix operator ``##`` can concatenate strings and identifiers into -one string. For example:: - - [revsetalias] - issue(a1) = grep(r'\bissue[ :]?' ## a1 ## r'\b|\bbug\(' ## a1 ## r'\)') - -``issue(1234)`` is equivalent to ``grep(r'\bissue[ :]?1234\b|\bbug\(1234\)')`` -in this case. This matches against all of "issue 1234", "issue:1234", -"issue1234" and "bug(1234)". - -All other prefix, infix and postfix operators have lower priority than -``##``. For example, ``a1 ## a2~2`` is equivalent to ``(a1 ## a2)~2``. +Equivalents +=========== Command line equivalents for :hg:`log`:: @@ -110,6 +130,9 @@ -P x -> !::x -l x -> limit(expr, x) +Examples +======== + Some sample queries: - Changesets on the default branch::
--- a/mercurial/help/templates.txt Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/help/templates.txt Tue Oct 18 14:15:15 2016 -0500 @@ -43,6 +43,15 @@ .. functionsmarker +We provide a limited set of infix arithmetic operations on integers:: + + + for addition + - for subtraction + * for multiplication + / for floor division (division rounded to integer nearest -infinity) + +Division fulfils the law x = x / y + mod(x, y). + Also, for any expression that returns a list, there is a list operator:: expr % "{template}" @@ -95,6 +104,10 @@ $ hg log -r 0 --template "files: {join(files, ', ')}\n" +- Join the list of files ending with ".py" with a ", ":: + + $ hg log -r 0 --template "pythonfiles: {join(files('**.py'), ', ')}\n" + - Separate non-empty arguments by a " ":: $ hg log -r 0 --template "{separate(' ', node, bookmarks, tags}\n"
--- a/mercurial/hg.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/hg.py Tue Oct 18 14:15:15 2016 -0500 @@ -195,7 +195,7 @@ return '' return os.path.basename(os.path.normpath(path)) -def share(ui, source, dest=None, update=True, bookmarks=True): +def share(ui, source, dest=None, update=True, bookmarks=True, defaultpath=None): '''create a shared repository''' if not islocal(source): @@ -240,10 +240,10 @@ destvfs.write('sharedpath', sharedpath) r = repository(ui, destwvfs.base) - postshare(srcrepo, r, bookmarks=bookmarks) + postshare(srcrepo, r, bookmarks=bookmarks, defaultpath=defaultpath) _postshareupdate(r, update, checkout=checkout) -def postshare(sourcerepo, destrepo, bookmarks=True): +def postshare(sourcerepo, destrepo, bookmarks=True, defaultpath=None): """Called after a new shared repo is created. The new repo only has a requirements file and pointer to the source. @@ -252,17 +252,18 @@ Extensions can wrap this function and write additional entries to destrepo/.hg/shared to indicate additional pieces of data to be shared. """ - default = sourcerepo.ui.config('paths', 'default') + default = defaultpath or sourcerepo.ui.config('paths', 'default') if default: fp = destrepo.vfs("hgrc", "w", text=True) fp.write("[paths]\n") fp.write("default = %s\n" % default) fp.close() - if bookmarks: - fp = destrepo.vfs('shared', 'w') - fp.write(sharedbookmarks + '\n') - fp.close() + with destrepo.wlock(): + if bookmarks: + fp = destrepo.vfs('shared', 'w') + fp.write(sharedbookmarks + '\n') + fp.close() def _postshareupdate(repo, update, checkout=None): """Maybe perform a working directory update after a shared repo is created. @@ -373,8 +374,15 @@ clone(ui, peeropts, source, dest=sharepath, pull=True, rev=rev, update=False, stream=stream) + # Resolve the value to put in [paths] section for the source. + if islocal(source): + defaultpath = os.path.abspath(util.urllocalpath(source)) + else: + defaultpath = source + sharerepo = repository(ui, path=sharepath) - share(ui, sharerepo, dest=dest, update=False, bookmarks=False) + share(ui, sharerepo, dest=dest, update=False, bookmarks=False, + defaultpath=defaultpath) # We need to perform a pull against the dest repo to fetch bookmarks # and other non-store data that isn't shared by default. In the case of @@ -737,20 +745,22 @@ if movemarkfrom == repo['.'].node(): pass # no-op update elif bookmarks.update(repo, [movemarkfrom], repo['.'].node()): - ui.status(_("updating bookmark %s\n") % repo._activebookmark) + b = ui.label(repo._activebookmark, 'bookmarks.active') + ui.status(_("updating bookmark %s\n") % b) else: # this can happen with a non-linear update - ui.status(_("(leaving bookmark %s)\n") % - repo._activebookmark) + b = ui.label(repo._activebookmark, 'bookmarks') + ui.status(_("(leaving bookmark %s)\n") % b) bookmarks.deactivate(repo) elif brev in repo._bookmarks: if brev != repo._activebookmark: - ui.status(_("(activating bookmark %s)\n") % brev) + b = ui.label(brev, 'bookmarks.active') + ui.status(_("(activating bookmark %s)\n") % b) bookmarks.activate(repo, brev) elif brev: if repo._activebookmark: - ui.status(_("(leaving bookmark %s)\n") % - repo._activebookmark) + b = ui.label(repo._activebookmark, 'bookmarks') + ui.status(_("(leaving bookmark %s)\n") % b) bookmarks.deactivate(repo) if warndest: @@ -758,10 +768,11 @@ return ret -def merge(repo, node, force=None, remind=True, mergeforce=False): +def merge(repo, node, force=None, remind=True, mergeforce=False, labels=None): """Branch merge with node, resolving changes. Return true if any unresolved conflicts.""" - stats = mergemod.update(repo, node, True, force, mergeforce=mergeforce) + stats = mergemod.update(repo, node, True, force, mergeforce=mergeforce, + labels=labels) _showstats(repo, stats) if stats[3]: repo.ui.status(_("use 'hg resolve' to retry unresolved file merges "
--- a/mercurial/hgweb/hgweb_mod.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/hgweb/hgweb_mod.py Tue Oct 18 14:15:15 2016 -0500 @@ -28,6 +28,7 @@ error, hg, hook, + profiling, repoview, templatefilters, templater, @@ -305,8 +306,9 @@ should be using instances of this class as the WSGI application. """ with self._obtainrepo() as repo: - for r in self._runwsgi(req, repo): - yield r + with profiling.maybeprofile(repo.ui): + for r in self._runwsgi(req, repo): + yield r def _runwsgi(self, req, repo): rctx = requestcontext(self, repo)
--- a/mercurial/hgweb/hgwebdir_mod.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/hgweb/hgwebdir_mod.py Tue Oct 18 14:15:15 2016 -0500 @@ -31,6 +31,7 @@ encoding, error, hg, + profiling, scmutil, templater, ui as uimod, @@ -217,6 +218,11 @@ return False def run_wsgi(self, req): + with profiling.maybeprofile(self.ui): + for r in self._runwsgi(req): + yield r + + def _runwsgi(self, req): try: self.refresh()
--- a/mercurial/hgweb/protocol.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/hgweb/protocol.py Tue Oct 18 14:15:15 2016 -0500 @@ -73,14 +73,30 @@ val = self.ui.fout.getvalue() self.ui.ferr, self.ui.fout = self.oldio return val - def groupchunks(self, cg): - z = zlib.compressobj() - while True: - chunk = cg.read(4096) - if not chunk: - break - yield z.compress(chunk) + + def groupchunks(self, fh): + def getchunks(): + while True: + chunk = fh.read(32768) + if not chunk: + break + yield chunk + + return self.compresschunks(getchunks()) + + def compresschunks(self, chunks): + # Don't allow untrusted settings because disabling compression or + # setting a very high compression level could lead to flooding + # the server's network or CPU. + z = zlib.compressobj(self.ui.configint('server', 'zliblevel', -1)) + for chunk in chunks: + data = z.compress(chunk) + # Not all calls to compress() emit data. It is cheaper to inspect + # that here than to send it via the generator. + if data: + yield data yield z.flush() + def _client(self): return 'remote:%s:%s:%s' % ( self.req.env.get('wsgi.url_scheme') or 'http',
--- a/mercurial/hgweb/server.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/hgweb/server.py Tue Oct 18 14:15:15 2016 -0500 @@ -263,7 +263,7 @@ return open(opt, 'a') return default -class MercurialHTTPServer(object, _mixin, httpservermod.httpserver): +class MercurialHTTPServer(_mixin, httpservermod.httpserver, object): # SO_REUSEADDR has broken semantics on windows if os.name == 'nt':
--- a/mercurial/hgweb/webcommands.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/hgweb/webcommands.py Tue Oct 18 14:15:15 2016 -0500 @@ -31,7 +31,6 @@ encoding, error, graphmod, - patch, revset, scmutil, templatefilters, @@ -861,8 +860,6 @@ fctx = webutil.filectx(web.repo, req) f = fctx.path() parity = paritygen(web.stripecount) - diffopts = patch.difffeatureopts(web.repo.ui, untrusted=True, - section='annotate', whitespace=True) def parents(f): for p in f.parents(): @@ -877,8 +874,8 @@ or 'application/octet-stream') lines = [((fctx.filectx(fctx.filerev()), 1), '(binary:%s)' % mt)] else: - lines = fctx.annotate(follow=True, linenumber=True, - diffopts=diffopts) + lines = webutil.annotate(fctx, web.repo.ui) + previousrev = None blockparitygen = paritygen(1) for lineno, ((f, targetline), l) in enumerate(lines):
--- a/mercurial/hgweb/webutil.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/hgweb/webutil.py Tue Oct 18 14:15:15 2016 -0500 @@ -164,6 +164,11 @@ def __len__(self): return len(self.siblings) +def annotate(fctx, ui): + diffopts = patch.difffeatureopts(ui, untrusted=True, + section='annotate', whitespace=True) + return fctx.annotate(follow=True, linenumber=True, diffopts=diffopts) + def parents(ctx, hide=None): if isinstance(ctx, context.basefilectx): introrev = ctx.introrev()
--- a/mercurial/httpconnection.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/httpconnection.py Tue Oct 18 14:15:15 2016 -0500 @@ -58,6 +58,12 @@ unit=_('kb'), total=self._total) return ret + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.close() + # moved here from url.py to avoid a cycle def readauthforuri(ui, uri, user): # Read configuration
--- a/mercurial/i18n.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/i18n.py Tue Oct 18 14:15:15 2016 -0500 @@ -12,7 +12,10 @@ import os import sys -from . import encoding +from . import ( + encoding, + pycompat, +) # modelled after templater.templatepath: if getattr(sys, 'frozen', None) is not None: @@ -27,10 +30,10 @@ _languages = None if (os.name == 'nt' - and 'LANGUAGE' not in os.environ - and 'LC_ALL' not in os.environ - and 'LC_MESSAGES' not in os.environ - and 'LANG' not in os.environ): + and 'LANGUAGE' not in encoding.environ + and 'LC_ALL' not in encoding.environ + and 'LC_MESSAGES' not in encoding.environ + and 'LANG' not in encoding.environ): # Try to detect UI language by "User Interface Language Management" API # if no locale variables are set. Note that locale.getdefaultlocale() # uses GetLocaleInfo(), which may be different from UI language. @@ -46,7 +49,7 @@ _ugettext = None def setdatapath(datapath): - localedir = os.path.join(datapath, 'locale') + localedir = os.path.join(datapath, pycompat.sysstr('locale')) t = gettextmod.translation('hg', localedir, _languages, fallback=True) global _ugettext try: @@ -85,16 +88,18 @@ # means u.encode(sys.getdefaultencoding()).decode(enc). Since # the Python encoding defaults to 'ascii', this fails if the # translated string use non-ASCII characters. - _msgcache[message] = u.encode(encoding.encoding, "replace") + encodingstr = pycompat.sysstr(encoding.encoding) + _msgcache[message] = u.encode(encodingstr, "replace") except LookupError: # An unknown encoding results in a LookupError. _msgcache[message] = message return _msgcache[message] def _plain(): - if 'HGPLAIN' not in os.environ and 'HGPLAINEXCEPT' not in os.environ: + if ('HGPLAIN' not in encoding.environ + and 'HGPLAINEXCEPT' not in encoding.environ): return False - exceptions = os.environ.get('HGPLAINEXCEPT', '').strip().split(',') + exceptions = encoding.environ.get('HGPLAINEXCEPT', '').strip().split(',') return 'i18n' not in exceptions if _plain():
--- a/mercurial/localrepo.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/localrepo.py Tue Oct 18 14:15:15 2016 -0500 @@ -149,14 +149,18 @@ def getbundle(self, source, heads=None, common=None, bundlecaps=None, **kwargs): - cg = exchange.getbundle(self._repo, source, heads=heads, - common=common, bundlecaps=bundlecaps, **kwargs) + chunks = exchange.getbundlechunks(self._repo, source, heads=heads, + common=common, bundlecaps=bundlecaps, + **kwargs) + cb = util.chunkbuffer(chunks) + if bundlecaps is not None and 'HG20' in bundlecaps: # When requesting a bundle2, getbundle returns a stream to make the # wire level function happier. We need to build a proper object # from it in local peer. - cg = bundle2.getunbundler(self.ui, cg) - return cg + return bundle2.getunbundler(self.ui, cb) + else: + return changegroup.getunbundler('01', cb, None) # TODO We might want to move the next two calls into legacypeer and add # unbundle instead. @@ -504,8 +508,9 @@ def manifest(self): return manifest.manifest(self.svfs) - def dirlog(self, dir): - return self.manifest.dirlog(dir) + @property + def manifestlog(self): + return manifest.manifestlog(self.svfs, self) @repofilecache('dirstate') def dirstate(self): @@ -1007,8 +1012,7 @@ def transaction(self, desc, report=None): if (self.ui.configbool('devel', 'all-warnings') or self.ui.configbool('devel', 'check-locks')): - l = self._lockref and self._lockref() - if l is None or not l.held: + if self._currentlock(self._lockref) is None: raise RuntimeError('programming error: transaction requires ' 'locking') tr = self.currenttransaction() @@ -1246,6 +1250,13 @@ delattr(self.unfiltered(), 'dirstate') def invalidate(self, clearfilecache=False): + '''Invalidates both store and non-store parts other than dirstate + + If a transaction is running, invalidation of store is omitted, + because discarding in-memory changes might cause inconsistency + (e.g. incomplete fncache causes unintentional failure, but + redundant one doesn't). + ''' unfiltered = self.unfiltered() # all file caches are stored unfiltered for k in self._filecache.keys(): # dirstate is invalidated separately in invalidatedirstate() @@ -1259,7 +1270,11 @@ except AttributeError: pass self.invalidatecaches() - self.store.invalidatecaches() + if not self.currenttransaction(): + # TODO: Changing contents of store outside transaction + # causes inconsistency. We should make in-memory store + # changes detectable, and abort if changed. + self.store.invalidatecaches() def invalidateall(self): '''Fully invalidates both store and non-store parts, causing the @@ -1268,6 +1283,7 @@ self.invalidate() self.invalidatedirstate() + @unfilteredmethod def _refreshfilecachestats(self, tr): """Reload stats of cached files so that they are flagged as valid""" for k, ce in self._filecache.items(): @@ -1290,8 +1306,15 @@ except error.LockHeld as inst: if not wait: raise - self.ui.warn(_("waiting for lock on %s held by %r\n") % - (desc, inst.locker)) + # show more details for new-style locks + if ':' in inst.locker: + host, pid = inst.locker.split(":", 1) + self.ui.warn( + _("waiting for lock on %s held by process %r " + "on host %r\n") % (desc, pid, host)) + else: + self.ui.warn(_("waiting for lock on %s held by %r\n") % + (desc, inst.locker)) # default to 600 seconds timeout l = lockmod.lock(vfs, lockname, int(self.ui.config("ui", "timeout", "600")), @@ -1320,8 +1343,8 @@ If both 'lock' and 'wlock' must be acquired, ensure you always acquires 'wlock' first to avoid a dead-lock hazard.''' - l = self._lockref and self._lockref() - if l is not None and l.held: + l = self._currentlock(self._lockref) + if l is not None: l.lock() return l @@ -1352,8 +1375,7 @@ # acquisition would not cause dead-lock as they would just fail. if wait and (self.ui.configbool('devel', 'all-warnings') or self.ui.configbool('devel', 'check-locks')): - l = self._lockref and self._lockref() - if l is not None and l.held: + if self._currentlock(self._lockref) is not None: self.ui.develwarn('"wlock" acquired after "lock"') def unlock(): @@ -1607,8 +1629,8 @@ ms = mergemod.mergestate.read(self) if list(ms.unresolved()): - raise error.Abort(_('unresolved merge conflicts ' - '(see "hg help resolve")')) + raise error.Abort(_("unresolved merge conflicts " + "(see 'hg help resolve')")) if ms.mdstate() != 's' or list(ms.driverresolved()): raise error.Abort(_('driver-resolved merge conflicts'), hint=_('run "hg resolve --all" to resolve')) @@ -1714,9 +1736,9 @@ drop = [f for f in removed if f in m] for f in drop: del m[f] - mn = self.manifest.add(m, trp, linkrev, - p1.manifestnode(), p2.manifestnode(), - added, drop) + mn = self.manifestlog.add(m, trp, linkrev, + p1.manifestnode(), p2.manifestnode(), + added, drop) files = changed + removed else: mn = p1.manifestnode()
--- a/mercurial/mail.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/mail.py Tue Oct 18 14:15:15 2016 -0500 @@ -8,6 +8,8 @@ from __future__ import absolute_import, print_function import email +import email.charset +import email.header import os import quopri import smtplib @@ -23,7 +25,7 @@ util, ) -_oldheaderinit = email.Header.Header.__init__ +_oldheaderinit = email.header.Header.__init__ def _unifiedheaderinit(self, *args, **kw): """ Python 2.7 introduces a backwards incompatible change @@ -203,24 +205,33 @@ raise error.Abort(_('%r specified as email transport, ' 'but not in PATH') % method) +def codec2iana(cs): + '''''' + cs = email.charset.Charset(cs).input_charset.lower() + + # "latin1" normalizes to "iso8859-1", standard calls for "iso-8859-1" + if cs.startswith("iso") and not cs.startswith("iso-"): + return "iso-" + cs[3:] + return cs + def mimetextpatch(s, subtype='plain', display=False): '''Return MIME message suitable for a patch. - Charset will be detected as utf-8 or (possibly fake) us-ascii. + Charset will be detected by first trying to decode as us-ascii, then utf-8, + and finally the global encodings. If all those fail, fall back to + ISO-8859-1, an encoding with that allows all byte sequences. Transfer encodings will be used if necessary.''' - cs = 'us-ascii' - if not display: + cs = ['us-ascii', 'utf-8', encoding.encoding, encoding.fallbackencoding] + if display: + return mimetextqp(s, subtype, 'us-ascii') + for charset in cs: try: - s.decode('us-ascii') + s.decode(charset) + return mimetextqp(s, subtype, codec2iana(charset)) except UnicodeDecodeError: - try: - s.decode('utf-8') - cs = 'utf-8' - except UnicodeDecodeError: - # We'll go with us-ascii as a fallback. - pass + pass - return mimetextqp(s, subtype, cs) + return mimetextqp(s, subtype, "iso-8859-1") def mimetextqp(body, subtype, charset): '''Return MIME message. @@ -279,7 +290,7 @@ if not display: # split into words? s, cs = _encode(ui, s, charsets) - return str(email.Header.Header(s, cs)) + return str(email.header.Header(s, cs)) return s def _addressencode(ui, name, addr, charsets=None): @@ -330,7 +341,7 @@ def headdecode(s): '''Decodes RFC-2047 header''' uparts = [] - for part, charset in email.Header.decode_header(s): + for part, charset in email.header.decode_header(s): if charset is not None: try: uparts.append(part.decode(charset))
--- a/mercurial/manifest.c Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/manifest.c Tue Oct 18 14:15:15 2016 -0500 @@ -56,10 +56,10 @@ } if (l->hash_suffix != '\0') { char newhash[21]; - memcpy(newhash, PyString_AsString(hash), 20); + memcpy(newhash, PyBytes_AsString(hash), 20); Py_DECREF(hash); newhash[20] = l->hash_suffix; - hash = PyString_FromStringAndSize(newhash, 21); + hash = PyBytes_FromStringAndSize(newhash, 21); } return hash; } @@ -79,7 +79,7 @@ if (!hash) return NULL; - flags = PyString_FromStringAndSize(s + hplen - 1, flen); + flags = PyBytes_FromStringAndSize(s + hplen - 1, flen); if (!flags) { Py_DECREF(hash); return NULL; @@ -144,7 +144,7 @@ if (!PyArg_ParseTuple(args, "S", &pydata)) { return -1; } - err = PyString_AsStringAndSize(pydata, &data, &len); + err = PyBytes_AsStringAndSize(pydata, &data, &len); self->dirty = false; if (err == -1) @@ -238,10 +238,10 @@ goto done; } pl = pathlen(l); - path = PyString_FromStringAndSize(l->start, pl); + path = PyBytes_FromStringAndSize(l->start, pl); hash = nodeof(l); consumed = pl + 41; - flags = PyString_FromStringAndSize(l->start + consumed, + flags = PyBytes_FromStringAndSize(l->start + consumed, l->len - consumed - 1); if (!path || !hash || !flags) { goto done; @@ -254,9 +254,15 @@ return ret; } +#ifdef IS_PY3K +#define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT +#else +#define LAZYMANIFESTENTRIESITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \ + | Py_TPFLAGS_HAVE_ITER +#endif + static PyTypeObject lazymanifestEntriesIterator = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "parsers.lazymanifest.entriesiterator", /*tp_name */ sizeof(lmIter), /*tp_basicsize */ 0, /*tp_itemsize */ @@ -275,9 +281,7 @@ 0, /*tp_getattro */ 0, /*tp_setattro */ 0, /*tp_as_buffer */ - /* tp_flags: Py_TPFLAGS_HAVE_ITER tells python to - use tp_iter and tp_iternext fields. */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, + LAZYMANIFESTENTRIESITERATOR_TPFLAGS, /* tp_flags */ "Iterator for 3-tuples in a lazymanifest.", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -295,12 +299,18 @@ return NULL; } pl = pathlen(l); - return PyString_FromStringAndSize(l->start, pl); + return PyBytes_FromStringAndSize(l->start, pl); } +#ifdef IS_PY3K +#define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT +#else +#define LAZYMANIFESTKEYSITERATOR_TPFLAGS Py_TPFLAGS_DEFAULT \ + | Py_TPFLAGS_HAVE_ITER +#endif + static PyTypeObject lazymanifestKeysIterator = { - PyObject_HEAD_INIT(NULL) - 0, /*ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "parsers.lazymanifest.keysiterator", /*tp_name */ sizeof(lmIter), /*tp_basicsize */ 0, /*tp_itemsize */ @@ -319,9 +329,7 @@ 0, /*tp_getattro */ 0, /*tp_setattro */ 0, /*tp_as_buffer */ - /* tp_flags: Py_TPFLAGS_HAVE_ITER tells python to - use tp_iter and tp_iternext fields. */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_ITER, + LAZYMANIFESTKEYSITERATOR_TPFLAGS, /* tp_flags */ "Keys iterator for a lazymanifest.", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ @@ -388,12 +396,12 @@ { line needle; line *hit; - if (!PyString_Check(key)) { + if (!PyBytes_Check(key)) { PyErr_Format(PyExc_TypeError, "getitem: manifest keys must be a string."); return NULL; } - needle.start = PyString_AsString(key); + needle.start = PyBytes_AsString(key); hit = bsearch(&needle, self->lines, self->numlines, sizeof(line), &linecmp); if (!hit || hit->deleted) { @@ -407,12 +415,12 @@ { line needle; line *hit; - if (!PyString_Check(key)) { + if (!PyBytes_Check(key)) { PyErr_Format(PyExc_TypeError, "delitem: manifest keys must be a string."); return -1; } - needle.start = PyString_AsString(key); + needle.start = PyBytes_AsString(key); hit = bsearch(&needle, self->lines, self->numlines, sizeof(line), &linecmp); if (!hit || hit->deleted) { @@ -476,7 +484,7 @@ char *dest; int i; line new; - if (!PyString_Check(key)) { + if (!PyBytes_Check(key)) { PyErr_Format(PyExc_TypeError, "setitem: manifest keys must be a string."); return -1; @@ -489,17 +497,17 @@ "Manifest values must be a tuple of (node, flags)."); return -1; } - if (PyString_AsStringAndSize(key, &path, &plen) == -1) { + if (PyBytes_AsStringAndSize(key, &path, &plen) == -1) { return -1; } pyhash = PyTuple_GetItem(value, 0); - if (!PyString_Check(pyhash)) { + if (!PyBytes_Check(pyhash)) { PyErr_Format(PyExc_TypeError, "node must be a 20-byte string"); return -1; } - hlen = PyString_Size(pyhash); + hlen = PyBytes_Size(pyhash); /* Some parts of the codebase try and set 21 or 22 * byte "hash" values in order to perturb things for * status. We have to preserve at least the 21st @@ -511,15 +519,15 @@ "node must be a 20-byte string"); return -1; } - hash = PyString_AsString(pyhash); + hash = PyBytes_AsString(pyhash); pyflags = PyTuple_GetItem(value, 1); - if (!PyString_Check(pyflags) || PyString_Size(pyflags) > 1) { + if (!PyBytes_Check(pyflags) || PyBytes_Size(pyflags) > 1) { PyErr_Format(PyExc_TypeError, "flags must a 0 or 1 byte string"); return -1; } - if (PyString_AsStringAndSize(pyflags, &flags, &flen) == -1) { + if (PyBytes_AsStringAndSize(pyflags, &flags, &flen) == -1) { return -1; } /* one null byte and one newline */ @@ -564,12 +572,12 @@ { line needle; line *hit; - if (!PyString_Check(key)) { + if (!PyBytes_Check(key)) { /* Our keys are always strings, so if the contains * check is for a non-string, just return false. */ return 0; } - needle.start = PyString_AsString(key); + needle.start = PyBytes_AsString(key); hit = bsearch(&needle, self->lines, self->numlines, sizeof(line), &linecmp); if (!hit || hit->deleted) { @@ -609,10 +617,10 @@ need += self->lines[i].len; } } - pydata = PyString_FromStringAndSize(NULL, need); + pydata = PyBytes_FromStringAndSize(NULL, need); if (!pydata) return -1; - data = PyString_AsString(pydata); + data = PyBytes_AsString(pydata); if (!data) { return -1; } @@ -747,7 +755,7 @@ return NULL; } listclean = (!pyclean) ? false : PyObject_IsTrue(pyclean); - es = PyString_FromString(""); + es = PyBytes_FromString(""); if (!es) { goto nomem; } @@ -787,8 +795,8 @@ result = linecmp(left, right); } key = result <= 0 ? - PyString_FromString(left->start) : - PyString_FromString(right->start); + PyBytes_FromString(left->start) : + PyBytes_FromString(right->start); if (!key) goto nomem; if (result < 0) { @@ -873,9 +881,14 @@ {NULL}, }; +#ifdef IS_PY3K +#define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT +#else +#define LAZYMANIFEST_TPFLAGS Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN +#endif + static PyTypeObject lazymanifestType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "parsers.lazymanifest", /* tp_name */ sizeof(lazymanifest), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -894,7 +907,7 @@ 0, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_SEQUENCE_IN, /* tp_flags */ + LAZYMANIFEST_TPFLAGS, /* tp_flags */ "TODO(augie)", /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */
--- a/mercurial/manifest.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/manifest.py Tue Oct 18 14:15:15 2016 -0500 @@ -104,69 +104,300 @@ _checkforbidden(files) return ''.join(lines) -class _lazymanifest(dict): - """This is the pure implementation of lazymanifest. +class lazymanifestiter(object): + def __init__(self, lm): + self.pos = 0 + self.lm = lm - It has not been optimized *at all* and is not lazy. - """ + def __iter__(self): + return self - def __init__(self, data): - dict.__init__(self) - for f, n, fl in _parse(data): - self[f] = n, fl + def next(self): + try: + data, pos = self.lm._get(self.pos) + except IndexError: + raise StopIteration + if pos == -1: + self.pos += 1 + return data[0] + self.pos += 1 + zeropos = data.find('\x00', pos) + return data[pos:zeropos] - def __setitem__(self, k, v): - node, flag = v - assert node is not None - if len(node) > 21: - node = node[:21] # match c implementation behavior - dict.__setitem__(self, k, (node, flag)) +class lazymanifestiterentries(object): + def __init__(self, lm): + self.lm = lm + self.pos = 0 def __iter__(self): - return iter(sorted(dict.keys(self))) + return self + + def next(self): + try: + data, pos = self.lm._get(self.pos) + except IndexError: + raise StopIteration + if pos == -1: + self.pos += 1 + return data + zeropos = data.find('\x00', pos) + hashval = unhexlify(data, self.lm.extrainfo[self.pos], + zeropos + 1, 40) + flags = self.lm._getflags(data, self.pos, zeropos) + self.pos += 1 + return (data[pos:zeropos], hashval, flags) + +def unhexlify(data, extra, pos, length): + s = data[pos:pos + length].decode('hex') + if extra: + s += chr(extra & 0xff) + return s + +def _cmp(a, b): + return (a > b) - (a < b) + +class _lazymanifest(object): + def __init__(self, data, positions=None, extrainfo=None, extradata=None): + if positions is None: + self.positions = self.findlines(data) + self.extrainfo = [0] * len(self.positions) + self.data = data + self.extradata = [] + else: + self.positions = positions[:] + self.extrainfo = extrainfo[:] + self.extradata = extradata[:] + self.data = data + + def findlines(self, data): + if not data: + return [] + pos = data.find("\n") + if pos == -1 or data[-1] != '\n': + raise ValueError("Manifest did not end in a newline.") + positions = [0] + prev = data[:data.find('\x00')] + while pos < len(data) - 1 and pos != -1: + positions.append(pos + 1) + nexts = data[pos + 1:data.find('\x00', pos + 1)] + if nexts < prev: + raise ValueError("Manifest lines not in sorted order.") + prev = nexts + pos = data.find("\n", pos + 1) + return positions + + def _get(self, index): + # get the position encoded in pos: + # positive number is an index in 'data' + # negative number is in extrapieces + pos = self.positions[index] + if pos >= 0: + return self.data, pos + return self.extradata[-pos - 1], -1 + + def _getkey(self, pos): + if pos >= 0: + return self.data[pos:self.data.find('\x00', pos + 1)] + return self.extradata[-pos - 1][0] + + def bsearch(self, key): + first = 0 + last = len(self.positions) - 1 + + while first <= last: + midpoint = (first + last)//2 + nextpos = self.positions[midpoint] + candidate = self._getkey(nextpos) + r = _cmp(key, candidate) + if r == 0: + return midpoint + else: + if r < 0: + last = midpoint - 1 + else: + first = midpoint + 1 + return -1 - def iterkeys(self): - return iter(sorted(dict.keys(self))) + def bsearch2(self, key): + # same as the above, but will always return the position + # done for performance reasons + first = 0 + last = len(self.positions) - 1 + + while first <= last: + midpoint = (first + last)//2 + nextpos = self.positions[midpoint] + candidate = self._getkey(nextpos) + r = _cmp(key, candidate) + if r == 0: + return (midpoint, True) + else: + if r < 0: + last = midpoint - 1 + else: + first = midpoint + 1 + return (first, False) + + def __contains__(self, key): + return self.bsearch(key) != -1 + + def _getflags(self, data, needle, pos): + start = pos + 41 + end = data.find("\n", start) + if end == -1: + end = len(data) - 1 + if start == end: + return '' + return self.data[start:end] - def iterentries(self): - return ((f, e[0], e[1]) for f, e in sorted(self.iteritems())) + def __getitem__(self, key): + if not isinstance(key, str): + raise TypeError("getitem: manifest keys must be a string.") + needle = self.bsearch(key) + if needle == -1: + raise KeyError + data, pos = self._get(needle) + if pos == -1: + return (data[1], data[2]) + zeropos = data.find('\x00', pos) + assert 0 <= needle <= len(self.positions) + assert len(self.extrainfo) == len(self.positions) + hashval = unhexlify(data, self.extrainfo[needle], zeropos + 1, 40) + flags = self._getflags(data, needle, zeropos) + return (hashval, flags) + + def __delitem__(self, key): + needle, found = self.bsearch2(key) + if not found: + raise KeyError + cur = self.positions[needle] + self.positions = self.positions[:needle] + self.positions[needle + 1:] + self.extrainfo = self.extrainfo[:needle] + self.extrainfo[needle + 1:] + if cur >= 0: + self.data = self.data[:cur] + '\x00' + self.data[cur + 1:] + + def __setitem__(self, key, value): + if not isinstance(key, str): + raise TypeError("setitem: manifest keys must be a string.") + if not isinstance(value, tuple) or len(value) != 2: + raise TypeError("Manifest values must be a tuple of (node, flags).") + hashval = value[0] + if not isinstance(hashval, str) or not 20 <= len(hashval) <= 22: + raise TypeError("node must be a 20-byte string") + flags = value[1] + if len(hashval) == 22: + hashval = hashval[:-1] + if not isinstance(flags, str) or len(flags) > 1: + raise TypeError("flags must a 0 or 1 byte string, got %r", flags) + needle, found = self.bsearch2(key) + if found: + # put the item + pos = self.positions[needle] + if pos < 0: + self.extradata[-pos - 1] = (key, hashval, value[1]) + else: + # just don't bother + self.extradata.append((key, hashval, value[1])) + self.positions[needle] = -len(self.extradata) + else: + # not found, put it in with extra positions + self.extradata.append((key, hashval, value[1])) + self.positions = (self.positions[:needle] + [-len(self.extradata)] + + self.positions[needle:]) + self.extrainfo = (self.extrainfo[:needle] + [0] + + self.extrainfo[needle:]) def copy(self): - c = _lazymanifest('') - c.update(self) - return c + # XXX call _compact like in C? + return _lazymanifest(self.data, self.positions, self.extrainfo, + self.extradata) + + def _compact(self): + # hopefully not called TOO often + if len(self.extradata) == 0: + return + l = [] + last_cut = 0 + i = 0 + offset = 0 + self.extrainfo = [0] * len(self.positions) + while i < len(self.positions): + if self.positions[i] >= 0: + cur = self.positions[i] + last_cut = cur + while True: + self.positions[i] = offset + i += 1 + if i == len(self.positions) or self.positions[i] < 0: + break + offset += self.positions[i] - cur + cur = self.positions[i] + end_cut = self.data.find('\n', cur) + if end_cut != -1: + end_cut += 1 + offset += end_cut - cur + l.append(self.data[last_cut:end_cut]) + else: + while i < len(self.positions) and self.positions[i] < 0: + cur = self.positions[i] + t = self.extradata[-cur - 1] + l.append(self._pack(t)) + self.positions[i] = offset + if len(t[1]) > 20: + self.extrainfo[i] = ord(t[1][21]) + offset += len(l[-1]) + i += 1 + self.data = ''.join(l) + self.extradata = [] + + def _pack(self, d): + return d[0] + '\x00' + d[1][:20].encode('hex') + d[2] + '\n' + + def text(self): + self._compact() + return self.data def diff(self, m2, clean=False): '''Finds changes between the current manifest and m2.''' + # XXX think whether efficiency matters here diff = {} - for fn, e1 in self.iteritems(): + for fn, e1, flags in self.iterentries(): if fn not in m2: - diff[fn] = e1, (None, '') + diff[fn] = (e1, flags), (None, '') else: e2 = m2[fn] - if e1 != e2: - diff[fn] = e1, e2 + if (e1, flags) != e2: + diff[fn] = (e1, flags), e2 elif clean: diff[fn] = None - for fn, e2 in m2.iteritems(): + for fn, e2, flags in m2.iterentries(): if fn not in self: - diff[fn] = (None, ''), e2 + diff[fn] = (None, ''), (e2, flags) return diff + def iterentries(self): + return lazymanifestiterentries(self) + + def iterkeys(self): + return lazymanifestiter(self) + + def __iter__(self): + return lazymanifestiter(self) + + def __len__(self): + return len(self.positions) + def filtercopy(self, filterfn): + # XXX should be optimized c = _lazymanifest('') for f, n, fl in self.iterentries(): if filterfn(f): c[f] = n, fl return c - def text(self): - """Get the full data of this manifest as a bytestring.""" - return _textv1(self.iterentries()) - try: _lazymanifest = parsers.lazymanifest except AttributeError: @@ -882,6 +1113,8 @@ def writesubtrees(self, m1, m2, writesubtree): self._load() # for consistency; should never have any effect here + m1._load() + m2._load() emptytree = treemanifest() for d, subm in self._dirs.iteritems(): subp1 = m1._dirs.get(d, emptytree)._node @@ -890,12 +1123,11 @@ subp1, subp2 = subp2, subp1 writesubtree(subm, subp1, subp2) -class manifest(revlog.revlog): +class manifestrevlog(revlog.revlog): + '''A revlog that stores manifest texts. This is responsible for caching the + full-text manifest contents. + ''' def __init__(self, opener, dir='', dirlogcache=None): - '''The 'dir' and 'dirlogcache' arguments are for internal use by - manifest.manifest only. External users should create a root manifest - log with manifest.manifest(opener) and call dirlog() on it. - ''' # During normal operations, we expect to deal with not more than four # revs at a time (such as during commit --amend). When rebasing large # stacks of commits, the number can go up, hence the config knob below. @@ -907,17 +1139,18 @@ cachesize = opts.get('manifestcachesize', cachesize) usetreemanifest = opts.get('treemanifest', usetreemanifest) usemanifestv2 = opts.get('manifestv2', usemanifestv2) - self._mancache = util.lrucachedict(cachesize) - self._treeinmem = usetreemanifest + self._treeondisk = usetreemanifest self._usemanifestv2 = usemanifestv2 + + self._fulltextcache = util.lrucachedict(cachesize) + indexfile = "00manifest.i" if dir: - assert self._treeondisk + assert self._treeondisk, 'opts is %r' % opts if not dir.endswith('/'): dir = dir + '/' indexfile = "meta/" + dir + "00manifest.i" - revlog.revlog.__init__(self, opener, indexfile) self._dir = dir # The dirlogcache is kept on the root manifest log if dir: @@ -925,12 +1158,281 @@ else: self._dirlogcache = {'': self} + super(manifestrevlog, self).__init__(opener, indexfile, + checkambig=bool(dir)) + + @property + def fulltextcache(self): + return self._fulltextcache + + def clearcaches(self): + super(manifestrevlog, self).clearcaches() + self._fulltextcache.clear() + self._dirlogcache = {'': self} + + def dirlog(self, dir): + if dir: + assert self._treeondisk + if dir not in self._dirlogcache: + self._dirlogcache[dir] = manifestrevlog(self.opener, dir, + self._dirlogcache) + return self._dirlogcache[dir] + + def add(self, m, transaction, link, p1, p2, added, removed): + if (p1 in self.fulltextcache and util.safehasattr(m, 'fastdelta') + and not self._usemanifestv2): + # If our first parent is in the manifest cache, we can + # compute a delta here using properties we know about the + # manifest up-front, which may save time later for the + # revlog layer. + + _checkforbidden(added) + # combine the changed lists into one sorted iterator + work = heapq.merge([(x, False) for x in added], + [(x, True) for x in removed]) + + arraytext, deltatext = m.fastdelta(self.fulltextcache[p1], work) + cachedelta = self.rev(p1), deltatext + text = util.buffer(arraytext) + n = self.addrevision(text, transaction, link, p1, p2, cachedelta) + else: + # The first parent manifest isn't already loaded, so we'll + # just encode a fulltext of the manifest and pass that + # through to the revlog layer, and let it handle the delta + # process. + if self._treeondisk: + m1 = self.read(p1) + m2 = self.read(p2) + n = self._addtree(m, transaction, link, m1, m2) + arraytext = None + else: + text = m.text(self._usemanifestv2) + n = self.addrevision(text, transaction, link, p1, p2) + arraytext = array.array('c', text) + + if arraytext is not None: + self.fulltextcache[n] = arraytext + + return n + + def _addtree(self, m, transaction, link, m1, m2): + # If the manifest is unchanged compared to one parent, + # don't write a new revision + if m.unmodifiedsince(m1) or m.unmodifiedsince(m2): + return m.node() + def writesubtree(subm, subp1, subp2): + sublog = self.dirlog(subm.dir()) + sublog.add(subm, transaction, link, subp1, subp2, None, None) + m.writesubtrees(m1, m2, writesubtree) + text = m.dirtext(self._usemanifestv2) + # Double-check whether contents are unchanged to one parent + if text == m1.dirtext(self._usemanifestv2): + n = m1.node() + elif text == m2.dirtext(self._usemanifestv2): + n = m2.node() + else: + n = self.addrevision(text, transaction, link, m1.node(), m2.node()) + # Save nodeid so parent manifest can calculate its nodeid + m.setnode(n) + return n + +class manifestlog(object): + """A collection class representing the collection of manifest snapshots + referenced by commits in the repository. + + In this situation, 'manifest' refers to the abstract concept of a snapshot + of the list of files in the given commit. Consumers of the output of this + class do not care about the implementation details of the actual manifests + they receive (i.e. tree or flat or lazily loaded, etc).""" + def __init__(self, opener, repo): + self._repo = repo + + usetreemanifest = False + + opts = getattr(opener, 'options', None) + if opts is not None: + usetreemanifest = opts.get('treemanifest', usetreemanifest) + self._treeinmem = usetreemanifest + + # We'll separate this into it's own cache once oldmanifest is no longer + # used + self._mancache = repo.manifest._mancache + + @property + def _revlog(self): + return self._repo.manifest + + def __getitem__(self, node): + """Retrieves the manifest instance for the given node. Throws a KeyError + if not found. + """ + if node in self._mancache: + cachemf = self._mancache[node] + # The old manifest may put non-ctx manifests in the cache, so skip + # those since they don't implement the full api. + if (isinstance(cachemf, manifestctx) or + isinstance(cachemf, treemanifestctx)): + return cachemf + + if self._treeinmem: + m = treemanifestctx(self._revlog, '', node) + else: + m = manifestctx(self._revlog, node) + if node != revlog.nullid: + self._mancache[node] = m + return m + + def add(self, m, transaction, link, p1, p2, added, removed): + return self._revlog.add(m, transaction, link, p1, p2, added, removed) + +class manifestctx(object): + """A class representing a single revision of a manifest, including its + contents, its parent revs, and its linkrev. + """ + def __init__(self, revlog, node): + self._revlog = revlog + self._data = None + + self._node = node + + # TODO: We eventually want p1, p2, and linkrev exposed on this class, + # but let's add it later when something needs it and we can load it + # lazily. + #self.p1, self.p2 = revlog.parents(node) + #rev = revlog.rev(node) + #self.linkrev = revlog.linkrev(rev) + + def node(self): + return self._node + + def read(self): + if not self._data: + if self._node == revlog.nullid: + self._data = manifestdict() + else: + text = self._revlog.revision(self._node) + arraytext = array.array('c', text) + self._revlog._fulltextcache[self._node] = arraytext + self._data = manifestdict(text) + return self._data + + def readfast(self): + rl = self._revlog + r = rl.rev(self._node) + deltaparent = rl.deltaparent(r) + if deltaparent != revlog.nullrev and deltaparent in rl.parentrevs(r): + return self.readdelta() + return self.read() + + def readdelta(self): + revlog = self._revlog + if revlog._usemanifestv2: + # Need to perform a slow delta + r0 = revlog.deltaparent(revlog.rev(self._node)) + m0 = manifestctx(revlog, revlog.node(r0)).read() + m1 = self.read() + md = manifestdict() + for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems(): + if n1: + md[f] = n1 + if fl1: + md.setflag(f, fl1) + return md + + r = revlog.rev(self._node) + d = mdiff.patchtext(revlog.revdiff(revlog.deltaparent(r), r)) + return manifestdict(d) + +class treemanifestctx(object): + def __init__(self, revlog, dir, node): + revlog = revlog.dirlog(dir) + self._revlog = revlog + self._dir = dir + self._data = None + + self._node = node + + # TODO: Load p1/p2/linkrev lazily. They need to be lazily loaded so that + # we can instantiate treemanifestctx objects for directories we don't + # have on disk. + #self.p1, self.p2 = revlog.parents(node) + #rev = revlog.rev(node) + #self.linkrev = revlog.linkrev(rev) + + def read(self): + if not self._data: + if self._node == revlog.nullid: + self._data = treemanifest() + elif self._revlog._treeondisk: + m = treemanifest(dir=self._dir) + def gettext(): + return self._revlog.revision(self._node) + def readsubtree(dir, subm): + return treemanifestctx(self._revlog, dir, subm).read() + m.read(gettext, readsubtree) + m.setnode(self._node) + self._data = m + else: + text = self._revlog.revision(self._node) + arraytext = array.array('c', text) + self._revlog.fulltextcache[self._node] = arraytext + self._data = treemanifest(dir=self._dir, text=text) + + return self._data + + def node(self): + return self._node + + def readdelta(self): + # Need to perform a slow delta + revlog = self._revlog + r0 = revlog.deltaparent(revlog.rev(self._node)) + m0 = treemanifestctx(revlog, self._dir, revlog.node(r0)).read() + m1 = self.read() + md = treemanifest(dir=self._dir) + for f, ((n0, fl0), (n1, fl1)) in m0.diff(m1).iteritems(): + if n1: + md[f] = n1 + if fl1: + md.setflag(f, fl1) + return md + + def readfast(self): + rl = self._revlog + r = rl.rev(self._node) + deltaparent = rl.deltaparent(r) + if deltaparent != revlog.nullrev and deltaparent in rl.parentrevs(r): + return self.readdelta() + return self.read() + +class manifest(manifestrevlog): + def __init__(self, opener, dir='', dirlogcache=None): + '''The 'dir' and 'dirlogcache' arguments are for internal use by + manifest.manifest only. External users should create a root manifest + log with manifest.manifest(opener) and call dirlog() on it. + ''' + # During normal operations, we expect to deal with not more than four + # revs at a time (such as during commit --amend). When rebasing large + # stacks of commits, the number can go up, hence the config knob below. + cachesize = 4 + usetreemanifest = False + opts = getattr(opener, 'options', None) + if opts is not None: + cachesize = opts.get('manifestcachesize', cachesize) + usetreemanifest = opts.get('treemanifest', usetreemanifest) + self._mancache = util.lrucachedict(cachesize) + self._treeinmem = usetreemanifest + super(manifest, self).__init__(opener, dir=dir, dirlogcache=dirlogcache) + def _newmanifest(self, data=''): if self._treeinmem: return treemanifest(self._dir, data) return manifestdict(data) def dirlog(self, dir): + """This overrides the base revlog implementation to allow construction + 'manifest' types instead of manifestrevlog types. This is only needed + until we migrate off the 'manifest' type.""" if dir: assert self._treeondisk if dir not in self._dirlogcache: @@ -973,20 +1475,6 @@ d = mdiff.patchtext(self.revdiff(self.deltaparent(r), r)) return manifestdict(d) - def readfast(self, node): - '''use the faster of readdelta or read - - This will return a manifest which is either only the files - added/modified relative to p1, or all files in the - manifest. Which one is returned depends on the codepath used - to retrieve the data. - ''' - r = self.rev(node) - deltaparent = self.deltaparent(r) - if deltaparent != revlog.nullrev and deltaparent in self.parentrevs(r): - return self.readdelta(node) - return self.read(node) - def readshallowfast(self, node): '''like readfast(), but calls readshallowdelta() instead of readdelta() ''' @@ -1000,7 +1488,11 @@ if node == revlog.nullid: return self._newmanifest() # don't upset local cache if node in self._mancache: - return self._mancache[node][0] + cached = self._mancache[node] + if (isinstance(cached, manifestctx) or + isinstance(cached, treemanifestctx)): + cached = cached.read() + return cached if self._treeondisk: def gettext(): return self.revision(node) @@ -1014,7 +1506,9 @@ text = self.revision(node) m = self._newmanifest(text) arraytext = array.array('c', text) - self._mancache[node] = (m, arraytext) + self._mancache[node] = m + if arraytext is not None: + self.fulltextcache[node] = arraytext return m def readshallow(self, node): @@ -1033,64 +1527,6 @@ except KeyError: return None, None - def add(self, m, transaction, link, p1, p2, added, removed): - if (p1 in self._mancache and not self._treeinmem - and not self._usemanifestv2): - # If our first parent is in the manifest cache, we can - # compute a delta here using properties we know about the - # manifest up-front, which may save time later for the - # revlog layer. - - _checkforbidden(added) - # combine the changed lists into one sorted iterator - work = heapq.merge([(x, False) for x in added], - [(x, True) for x in removed]) - - arraytext, deltatext = m.fastdelta(self._mancache[p1][1], work) - cachedelta = self.rev(p1), deltatext - text = util.buffer(arraytext) - n = self.addrevision(text, transaction, link, p1, p2, cachedelta) - else: - # The first parent manifest isn't already loaded, so we'll - # just encode a fulltext of the manifest and pass that - # through to the revlog layer, and let it handle the delta - # process. - if self._treeondisk: - m1 = self.read(p1) - m2 = self.read(p2) - n = self._addtree(m, transaction, link, m1, m2) - arraytext = None - else: - text = m.text(self._usemanifestv2) - n = self.addrevision(text, transaction, link, p1, p2) - arraytext = array.array('c', text) - - self._mancache[n] = (m, arraytext) - - return n - - def _addtree(self, m, transaction, link, m1, m2): - # If the manifest is unchanged compared to one parent, - # don't write a new revision - if m.unmodifiedsince(m1) or m.unmodifiedsince(m2): - return m.node() - def writesubtree(subm, subp1, subp2): - sublog = self.dirlog(subm.dir()) - sublog.add(subm, transaction, link, subp1, subp2, None, None) - m.writesubtrees(m1, m2, writesubtree) - text = m.dirtext(self._usemanifestv2) - # Double-check whether contents are unchanged to one parent - if text == m1.dirtext(self._usemanifestv2): - n = m1.node() - elif text == m2.dirtext(self._usemanifestv2): - n = m2.node() - else: - n = self.addrevision(text, transaction, link, m1.node(), m2.node()) - # Save nodeid so parent manifest can calculate its nodeid - m.setnode(n) - return n - def clearcaches(self): super(manifest, self).clearcaches() self._mancache.clear() - self._dirlogcache = {'': self}
--- a/mercurial/mdiff.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/mdiff.py Tue Oct 18 14:15:15 2016 -0500 @@ -113,12 +113,11 @@ s1 = i1 s2 = i2 -def allblocks(text1, text2, opts=None, lines1=None, lines2=None, refine=False): +def allblocks(text1, text2, opts=None, lines1=None, lines2=None): """Return (block, type) tuples, where block is an mdiff.blocks line entry. type is '=' for blocks matching exactly one another (bdiff blocks), '!' for non-matching blocks and '~' for blocks - matching only after having filtered blank lines. If refine is True, - then '~' blocks are refined and are only made of blank lines. + matching only after having filtered blank lines. line1 and line2 are text1 and text2 split with splitnewlines() if they are already available. """
--- a/mercurial/merge.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/merge.py Tue Oct 18 14:15:15 2016 -0500 @@ -475,10 +475,12 @@ flo = fco.flags() fla = fca.flags() if 'x' in flags + flo + fla and 'l' not in flags + flo + fla: - if fca.node() == nullid: + if fca.node() == nullid and flags != flo: if preresolve: self._repo.ui.warn( - _('warning: cannot merge flags for %s\n') % afile) + _('warning: cannot merge flags for %s ' + 'without common ancestor - keeping local flags\n') + % afile) elif flags == fla: flags = flo if preresolve: @@ -781,7 +783,7 @@ def manifestmerge(repo, wctx, p2, pa, branchmerge, force, matcher, acceptremote, followcopies): """ - Merge p1 and p2 with ancestor pa and generate merge action list + Merge wctx and p2 with ancestor pa and generate merge action list branchmerge and force are as passed in to update matcher = matcher to filter file lists @@ -1036,6 +1038,12 @@ unlink = util.unlinkpath wjoin = repo.wjoin audit = repo.wvfs.audit + try: + cwd = os.getcwd() + except OSError as err: + if err.errno != errno.ENOENT: + raise + cwd = None i = 0 for f, args, msg in actions: repo.ui.debug(" %s: %s -> r\n" % (f, msg)) @@ -1053,6 +1061,18 @@ i += 1 if i > 0: yield i, f + if cwd: + # cwd was present before we started to remove files + # let's check if it is present after we removed them + try: + os.getcwd() + except OSError as err: + if err.errno != errno.ENOENT: + raise + # Print a warning if cwd was deleted + repo.ui.warn(_("current directory was removed\n" + "(consider changing to repo root: %s)\n") % + repo.root) def batchget(repo, mctx, actions): """apply gets to the working directory @@ -1150,7 +1170,7 @@ numupdates = sum(len(l) for m, l in actions.items() if m != 'k') if [a for a in actions['r'] if a[0] == '.hgsubstate']: - subrepo.submerge(repo, wctx, mctx, wctx, overwrite) + subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels) # remove in parallel (must come first) z = 0 @@ -1168,7 +1188,7 @@ updated = len(actions['g']) if [a for a in actions['g'] if a[0] == '.hgsubstate']: - subrepo.submerge(repo, wctx, mctx, wctx, overwrite) + subrepo.submerge(repo, wctx, mctx, wctx, overwrite, labels) # forget (manifest only, just log it) (must come first) for f, args, msg in actions['f']: @@ -1253,7 +1273,7 @@ progress(_updating, z, item=f, total=numupdates, unit=_files) if f == '.hgsubstate': # subrepo states need updating subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx), - overwrite) + overwrite, labels) continue audit(f) complete, r = ms.preresolve(f, wctx) @@ -1286,8 +1306,29 @@ removed += msremoved extraactions = ms.actions() - for k, acts in extraactions.iteritems(): - actions[k].extend(acts) + if extraactions: + mfiles = set(a[0] for a in actions['m']) + for k, acts in extraactions.iteritems(): + actions[k].extend(acts) + # Remove these files from actions['m'] as well. This is important + # because in recordupdates, files in actions['m'] are processed + # after files in other actions, and the merge driver might add + # files to those actions via extraactions above. This can lead to a + # file being recorded twice, with poor results. This is especially + # problematic for actions['r'] (currently only possible with the + # merge driver in the initial merge process; interrupted merges + # don't go through this flow). + # + # The real fix here is to have indexes by both file and action so + # that when the action for a file is changed it is automatically + # reflected in the other action lists. But that involves a more + # complex data structure, so this will do for now. + # + # We don't need to do the same operation for 'dc' and 'cd' because + # those lists aren't consulted again. + mfiles.difference_update(a[0] for a in acts) + + actions['m'] = [a for a in actions['m'] if a[0] in mfiles] progress(_updating, None, total=numupdates, unit=_files) @@ -1514,15 +1555,16 @@ pas = [p1] # deprecated config: merge.followcopies - followcopies = False + followcopies = repo.ui.configbool('merge', 'followcopies', True) if overwrite: pas = [wc] + followcopies = False elif pas == [p2]: # backwards - pas = [wc.p1()] - elif not branchmerge and not wc.dirty(missing=True): - pass - elif pas[0] and repo.ui.configbool('merge', 'followcopies', True): - followcopies = True + pas = [p1] + elif not pas[0]: + followcopies = False + if not branchmerge and not wc.dirty(missing=True): + followcopies = False ### calculate phase actionbyfile, diverge, renamedelete = calculateupdates( @@ -1535,11 +1577,13 @@ if '.hgsubstate' in actionbyfile: f = '.hgsubstate' m, args, msg = actionbyfile[f] + prompts = filemerge.partextras(labels) + prompts['f'] = f if m == 'cd': if repo.ui.promptchoice( - _("local changed %s which remote deleted\n" + _("local%(l)s changed %(f)s which other%(o)s deleted\n" "use (c)hanged version or (d)elete?" - "$$ &Changed $$ &Delete") % f, 0): + "$$ &Changed $$ &Delete") % prompts, 0): actionbyfile[f] = ('r', None, "prompt delete") elif f in p1: actionbyfile[f] = ('am', None, "prompt keep") @@ -1549,9 +1593,9 @@ f1, f2, fa, move, anc = args flags = p2[f2].flags() if repo.ui.promptchoice( - _("remote changed %s which local deleted\n" + _("other%(o)s changed %(f)s which local%(l)s deleted\n" "use (c)hanged version or leave (d)eleted?" - "$$ &Changed $$ &Deleted") % f, 0) == 0: + "$$ &Changed $$ &Deleted") % prompts, 0) == 0: actionbyfile[f] = ('g', (flags, False), "prompt recreating") else: del actionbyfile[f] @@ -1563,7 +1607,7 @@ actions[m] = [] actions[m].append((f, args, msg)) - if not util.checkcase(repo.path): + if not util.fscasesensitive(repo.path): # check collision between files only in p2 for clean update if (not branchmerge and (force or not wc.dirty(missing=True, branch=False))):
--- a/mercurial/mpatch.c Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/mpatch.c Tue Oct 18 14:15:15 2016 -0500 @@ -20,49 +20,33 @@ of the GNU General Public License, incorporated herein by reference. */ -#define PY_SSIZE_T_CLEAN -#include <Python.h> #include <stdlib.h> #include <string.h> -#include "util.h" #include "bitmanipulation.h" - -static char mpatch_doc[] = "Efficient binary patching."; -static PyObject *mpatch_Error; +#include "compat.h" +#include "mpatch.h" -struct frag { - int start, end, len; - const char *data; -}; - -struct flist { - struct frag *base, *head, *tail; -}; - -static struct flist *lalloc(Py_ssize_t size) +static struct mpatch_flist *lalloc(ssize_t size) { - struct flist *a = NULL; + struct mpatch_flist *a = NULL; if (size < 1) size = 1; - a = (struct flist *)malloc(sizeof(struct flist)); + a = (struct mpatch_flist *)malloc(sizeof(struct mpatch_flist)); if (a) { - a->base = (struct frag *)malloc(sizeof(struct frag) * size); + a->base = (struct mpatch_frag *)malloc(sizeof(struct mpatch_frag) * size); if (a->base) { a->head = a->tail = a->base; return a; } free(a); - a = NULL; } - if (!PyErr_Occurred()) - PyErr_NoMemory(); return NULL; } -static void lfree(struct flist *a) +void mpatch_lfree(struct mpatch_flist *a) { if (a) { free(a->base); @@ -70,7 +54,7 @@ } } -static Py_ssize_t lsize(struct flist *a) +static ssize_t lsize(struct mpatch_flist *a) { return a->tail - a->head; } @@ -78,9 +62,10 @@ /* move hunks in source that are less cut to dest, compensating for changes in offset. the last hunk may be split if necessary. */ -static int gather(struct flist *dest, struct flist *src, int cut, int offset) +static int gather(struct mpatch_flist *dest, struct mpatch_flist *src, int cut, + int offset) { - struct frag *d = dest->tail, *s = src->head; + struct mpatch_frag *d = dest->tail, *s = src->head; int postend, c, l; while (s != src->tail) { @@ -123,9 +108,9 @@ } /* like gather, but with no output list */ -static int discard(struct flist *src, int cut, int offset) +static int discard(struct mpatch_flist *src, int cut, int offset) { - struct frag *s = src->head; + struct mpatch_frag *s = src->head; int postend, c, l; while (s != src->tail) { @@ -160,10 +145,11 @@ /* combine hunk lists a and b, while adjusting b for offset changes in a/ this deletes a and b and returns the resultant list. */ -static struct flist *combine(struct flist *a, struct flist *b) +static struct mpatch_flist *combine(struct mpatch_flist *a, + struct mpatch_flist *b) { - struct flist *c = NULL; - struct frag *bh, *ct; + struct mpatch_flist *c = NULL; + struct mpatch_frag *bh, *ct; int offset = 0, post; if (a && b) @@ -189,26 +175,26 @@ } /* hold on to tail from a */ - memcpy(c->tail, a->head, sizeof(struct frag) * lsize(a)); + memcpy(c->tail, a->head, sizeof(struct mpatch_frag) * lsize(a)); c->tail += lsize(a); } - lfree(a); - lfree(b); + mpatch_lfree(a); + mpatch_lfree(b); return c; } /* decode a binary patch into a hunk list */ -static struct flist *decode(const char *bin, Py_ssize_t len) +int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist **res) { - struct flist *l; - struct frag *lt; + struct mpatch_flist *l; + struct mpatch_frag *lt; int pos = 0; /* assume worst case size, we won't have many of these lists */ l = lalloc(len / 12 + 1); if (!l) - return NULL; + return MPATCH_ERR_NO_MEM; lt = l->tail; @@ -224,28 +210,24 @@ } if (pos != len) { - if (!PyErr_Occurred()) - PyErr_SetString(mpatch_Error, "patch cannot be decoded"); - lfree(l); - return NULL; + mpatch_lfree(l); + return MPATCH_ERR_CANNOT_BE_DECODED; } l->tail = lt; - return l; + *res = l; + return 0; } /* calculate the size of resultant text */ -static Py_ssize_t calcsize(Py_ssize_t len, struct flist *l) +ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l) { - Py_ssize_t outlen = 0, last = 0; - struct frag *f = l->head; + ssize_t outlen = 0, last = 0; + struct mpatch_frag *f = l->head; while (f != l->tail) { if (f->start < last || f->end > len) { - if (!PyErr_Occurred()) - PyErr_SetString(mpatch_Error, - "invalid patch"); - return -1; + return MPATCH_ERR_INVALID_PATCH; } outlen += f->start - last; last = f->end; @@ -257,18 +239,16 @@ return outlen; } -static int apply(char *buf, const char *orig, Py_ssize_t len, struct flist *l) +int mpatch_apply(char *buf, const char *orig, ssize_t len, + struct mpatch_flist *l) { - struct frag *f = l->head; + struct mpatch_frag *f = l->head; int last = 0; char *p = buf; while (f != l->tail) { if (f->start < last || f->end > len) { - if (!PyErr_Occurred()) - PyErr_SetString(mpatch_Error, - "invalid patch"); - return 0; + return MPATCH_ERR_INVALID_PATCH; } memcpy(p, orig + last, f->start - last); p += f->start - last; @@ -278,146 +258,23 @@ f++; } memcpy(p, orig + last, len - last); - return 1; + return 0; } /* recursively generate a patch of all bins between start and end */ -static struct flist *fold(PyObject *bins, Py_ssize_t start, Py_ssize_t end) +struct mpatch_flist *mpatch_fold(void *bins, + struct mpatch_flist* (*get_next_item)(void*, ssize_t), + ssize_t start, ssize_t end) { - Py_ssize_t len, blen; - const char *buffer; + ssize_t len; if (start + 1 == end) { /* trivial case, output a decoded list */ - PyObject *tmp = PyList_GetItem(bins, start); - if (!tmp) - return NULL; - if (PyObject_AsCharBuffer(tmp, &buffer, &blen)) - return NULL; - return decode(buffer, blen); + return get_next_item(bins, start); } /* divide and conquer, memory management is elsewhere */ len = (end - start) / 2; - return combine(fold(bins, start, start + len), - fold(bins, start + len, end)); -} - -static PyObject * -patches(PyObject *self, PyObject *args) -{ - PyObject *text, *bins, *result; - struct flist *patch; - const char *in; - char *out; - Py_ssize_t len, outlen, inlen; - - if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins)) - return NULL; - - len = PyList_Size(bins); - if (!len) { - /* nothing to do */ - Py_INCREF(text); - return text; - } - - if (PyObject_AsCharBuffer(text, &in, &inlen)) - return NULL; - - patch = fold(bins, 0, len); - if (!patch) - return NULL; - - outlen = calcsize(inlen, patch); - if (outlen < 0) { - result = NULL; - goto cleanup; - } - result = PyBytes_FromStringAndSize(NULL, outlen); - if (!result) { - result = NULL; - goto cleanup; - } - out = PyBytes_AsString(result); - if (!apply(out, in, inlen, patch)) { - Py_DECREF(result); - result = NULL; - } -cleanup: - lfree(patch); - return result; + return combine(mpatch_fold(bins, get_next_item, start, start + len), + mpatch_fold(bins, get_next_item, start + len, end)); } - -/* calculate size of a patched file directly */ -static PyObject * -patchedsize(PyObject *self, PyObject *args) -{ - long orig, start, end, len, outlen = 0, last = 0, pos = 0; - Py_ssize_t patchlen; - char *bin; - - if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen)) - return NULL; - - while (pos >= 0 && pos < patchlen) { - start = getbe32(bin + pos); - end = getbe32(bin + pos + 4); - len = getbe32(bin + pos + 8); - if (start > end) - break; /* sanity check */ - pos += 12 + len; - outlen += start - last; - last = end; - outlen += len; - } - - if (pos != patchlen) { - if (!PyErr_Occurred()) - PyErr_SetString(mpatch_Error, "patch cannot be decoded"); - return NULL; - } - - outlen += orig - last; - return Py_BuildValue("l", outlen); -} - -static PyMethodDef methods[] = { - {"patches", patches, METH_VARARGS, "apply a series of patches\n"}, - {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"}, - {NULL, NULL} -}; - -#ifdef IS_PY3K -static struct PyModuleDef mpatch_module = { - PyModuleDef_HEAD_INIT, - "mpatch", - mpatch_doc, - -1, - methods -}; - -PyMODINIT_FUNC PyInit_mpatch(void) -{ - PyObject *m; - - m = PyModule_Create(&mpatch_module); - if (m == NULL) - return NULL; - - mpatch_Error = PyErr_NewException("mercurial.mpatch.mpatchError", - NULL, NULL); - Py_INCREF(mpatch_Error); - PyModule_AddObject(m, "mpatchError", mpatch_Error); - - return m; -} -#else -PyMODINIT_FUNC -initmpatch(void) -{ - Py_InitModule3("mpatch", methods, mpatch_doc); - mpatch_Error = PyErr_NewException("mercurial.mpatch.mpatchError", - NULL, NULL); -} -#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/mpatch.h Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,26 @@ +#ifndef _HG_MPATCH_H_ +#define _HG_MPATCH_H_ + +#define MPATCH_ERR_NO_MEM -3 +#define MPATCH_ERR_CANNOT_BE_DECODED -2 +#define MPATCH_ERR_INVALID_PATCH -1 + +struct mpatch_frag { + int start, end, len; + const char *data; +}; + +struct mpatch_flist { + struct mpatch_frag *base, *head, *tail; +}; + +int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist** res); +ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l); +void mpatch_lfree(struct mpatch_flist *a); +int mpatch_apply(char *buf, const char *orig, ssize_t len, + struct mpatch_flist *l); +struct mpatch_flist *mpatch_fold(void *bins, + struct mpatch_flist* (*get_next_item)(void*, ssize_t), + ssize_t start, ssize_t end); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/mpatch_module.c Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,195 @@ +/* + mpatch.c - efficient binary patching for Mercurial + + This implements a patch algorithm that's O(m + nlog n) where m is the + size of the output and n is the number of patches. + + Given a list of binary patches, it unpacks each into a hunk list, + then combines the hunk lists with a treewise recursion to form a + single hunk list. This hunk list is then applied to the original + text. + + The text (or binary) fragments are copied directly from their source + Python objects into a preallocated output string to avoid the + allocation of intermediate Python objects. Working memory is about 2x + the total number of hunks. + + Copyright 2005, 2006 Matt Mackall <mpm@selenic.com> + + This software may be used and distributed according to the terms + of the GNU General Public License, incorporated herein by reference. +*/ + +#define PY_SSIZE_T_CLEAN +#include <Python.h> +#include <stdlib.h> +#include <string.h> + +#include "util.h" +#include "bitmanipulation.h" +#include "compat.h" +#include "mpatch.h" + +static char mpatch_doc[] = "Efficient binary patching."; +static PyObject *mpatch_Error; + +static void setpyerr(int r) +{ + switch (r) { + case MPATCH_ERR_NO_MEM: + PyErr_NoMemory(); + break; + case MPATCH_ERR_CANNOT_BE_DECODED: + PyErr_SetString(mpatch_Error, "patch cannot be decoded"); + break; + case MPATCH_ERR_INVALID_PATCH: + PyErr_SetString(mpatch_Error, "invalid patch"); + break; + } +} + +struct mpatch_flist *cpygetitem(void *bins, ssize_t pos) +{ + const char *buffer; + struct mpatch_flist *res; + ssize_t blen; + int r; + + PyObject *tmp = PyList_GetItem((PyObject*)bins, pos); + if (!tmp) + return NULL; + if (PyObject_AsCharBuffer(tmp, &buffer, (Py_ssize_t*)&blen)) + return NULL; + if ((r = mpatch_decode(buffer, blen, &res)) < 0) { + if (!PyErr_Occurred()) + setpyerr(r); + return NULL; + } + return res; +} + +static PyObject * +patches(PyObject *self, PyObject *args) +{ + PyObject *text, *bins, *result; + struct mpatch_flist *patch; + const char *in; + int r = 0; + char *out; + Py_ssize_t len, outlen, inlen; + + if (!PyArg_ParseTuple(args, "OO:mpatch", &text, &bins)) + return NULL; + + len = PyList_Size(bins); + if (!len) { + /* nothing to do */ + Py_INCREF(text); + return text; + } + + if (PyObject_AsCharBuffer(text, &in, &inlen)) + return NULL; + + patch = mpatch_fold(bins, cpygetitem, 0, len); + if (!patch) { /* error already set or memory error */ + if (!PyErr_Occurred()) + PyErr_NoMemory(); + return NULL; + } + + outlen = mpatch_calcsize(inlen, patch); + if (outlen < 0) { + r = (int)outlen; + result = NULL; + goto cleanup; + } + result = PyBytes_FromStringAndSize(NULL, outlen); + if (!result) { + result = NULL; + goto cleanup; + } + out = PyBytes_AsString(result); + if ((r = mpatch_apply(out, in, inlen, patch)) < 0) { + Py_DECREF(result); + result = NULL; + } +cleanup: + mpatch_lfree(patch); + if (!result && !PyErr_Occurred()) + setpyerr(r); + return result; +} + +/* calculate size of a patched file directly */ +static PyObject * +patchedsize(PyObject *self, PyObject *args) +{ + long orig, start, end, len, outlen = 0, last = 0, pos = 0; + Py_ssize_t patchlen; + char *bin; + + if (!PyArg_ParseTuple(args, "ls#", &orig, &bin, &patchlen)) + return NULL; + + while (pos >= 0 && pos < patchlen) { + start = getbe32(bin + pos); + end = getbe32(bin + pos + 4); + len = getbe32(bin + pos + 8); + if (start > end) + break; /* sanity check */ + pos += 12 + len; + outlen += start - last; + last = end; + outlen += len; + } + + if (pos != patchlen) { + if (!PyErr_Occurred()) + PyErr_SetString(mpatch_Error, "patch cannot be decoded"); + return NULL; + } + + outlen += orig - last; + return Py_BuildValue("l", outlen); +} + +static PyMethodDef methods[] = { + {"patches", patches, METH_VARARGS, "apply a series of patches\n"}, + {"patchedsize", patchedsize, METH_VARARGS, "calculed patched size\n"}, + {NULL, NULL} +}; + +#ifdef IS_PY3K +static struct PyModuleDef mpatch_module = { + PyModuleDef_HEAD_INIT, + "mpatch", + mpatch_doc, + -1, + methods +}; + +PyMODINIT_FUNC PyInit_mpatch(void) +{ + PyObject *m; + + m = PyModule_Create(&mpatch_module); + if (m == NULL) + return NULL; + + mpatch_Error = PyErr_NewException("mercurial.mpatch.mpatchError", + NULL, NULL); + Py_INCREF(mpatch_Error); + PyModule_AddObject(m, "mpatchError", mpatch_Error); + + return m; +} +#else +PyMODINIT_FUNC +initmpatch(void) +{ + Py_InitModule3("mpatch", methods, mpatch_doc); + mpatch_Error = PyErr_NewException("mercurial.mpatch.mpatchError", + NULL, NULL); +} +#endif
--- a/mercurial/obsolete.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/obsolete.py Tue Oct 18 14:15:15 2016 -0500 @@ -42,9 +42,9 @@ (A, ()) -- When changeset A is split into B and C, a single marker are used: +- When changeset A is split into B and C, a single marker is used: - (A, (C, C)) + (A, (B, C)) We use a single marker to distinguish the "split" case from the "divergence" case. If two independent operations rewrite the same changeset A in to A' and @@ -1236,7 +1236,7 @@ if not prec.mutable(): raise error.Abort(_("cannot obsolete public changeset: %s") % prec, - hint='see "hg help phases" for details') + hint="see 'hg help phases' for details") nprec = prec.node() nsucs = tuple(s.node() for s in sucs) npare = None
--- a/mercurial/osutil.c Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/osutil.c Tue Oct 18 14:15:15 2016 -0500 @@ -63,11 +63,19 @@ }; #endif +#ifdef IS_PY3K +#define listdir_slot(name) \ + static PyObject *listdir_stat_##name(PyObject *self, void *x) \ + { \ + return PyLong_FromLong(((struct listdir_stat *)self)->st.name); \ + } +#else #define listdir_slot(name) \ static PyObject *listdir_stat_##name(PyObject *self, void *x) \ { \ return PyInt_FromLong(((struct listdir_stat *)self)->st.name); \ } +#endif listdir_slot(st_dev) listdir_slot(st_mode) @@ -624,7 +632,7 @@ pypath = PySequence_GetItem(names, i); if (!pypath) goto bail; - path = PyString_AsString(pypath); + path = PyBytes_AsString(pypath); if (path == NULL) { Py_DECREF(pypath); PyErr_SetString(PyExc_TypeError, "not a string"); @@ -706,7 +714,7 @@ if (!rfdslist) goto bail; for (i = 0; i < rfdscount; i++) { - PyObject *obj = PyInt_FromLong(rfds[i]); + PyObject *obj = PyLong_FromLong(rfds[i]); if (!obj) goto bail; PyList_SET_ITEM(rfdslist, i, obj);
--- a/mercurial/parser.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/parser.py Tue Oct 18 14:15:15 2016 -0500 @@ -65,7 +65,7 @@ # handle infix rules, take as suffix if unambiguous infix, suffix = self._elements[token][3:] if suffix and not (infix and self._hasnewterm()): - expr = (suffix[0], expr) + expr = (suffix, expr) elif infix: expr = (infix[0], expr, self._parseoperand(*infix[1:])) else:
--- a/mercurial/parsers.c Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/parsers.c Tue Oct 18 14:15:15 2016 -0500 @@ -15,6 +15,18 @@ #include "util.h" #include "bitmanipulation.h" +#ifdef IS_PY3K +/* The mapping of Python types is meant to be temporary to get Python + * 3 to compile. We should remove this once Python 3 support is fully + * supported and proper types are used in the extensions themselves. */ +#define PyInt_Type PyLong_Type +#define PyInt_Check PyLong_Check +#define PyInt_FromLong PyLong_FromLong +#define PyInt_FromSsize_t PyLong_FromSsize_t +#define PyInt_AS_LONG PyLong_AS_LONG +#define PyInt_AsLong PyLong_AsLong +#endif + static char *versionerrortext = "Python minor version mismatch"; static int8_t hextable[256] = { @@ -610,37 +622,37 @@ /* Figure out how much we need to allocate. */ for (nbytes = 40, pos = 0; PyDict_Next(map, &pos, &k, &v);) { PyObject *c; - if (!PyString_Check(k)) { + if (!PyBytes_Check(k)) { PyErr_SetString(PyExc_TypeError, "expected string key"); goto bail; } - nbytes += PyString_GET_SIZE(k) + 17; + nbytes += PyBytes_GET_SIZE(k) + 17; c = PyDict_GetItem(copymap, k); if (c) { - if (!PyString_Check(c)) { + if (!PyBytes_Check(c)) { PyErr_SetString(PyExc_TypeError, "expected string key"); goto bail; } - nbytes += PyString_GET_SIZE(c) + 1; + nbytes += PyBytes_GET_SIZE(c) + 1; } } - packobj = PyString_FromStringAndSize(NULL, nbytes); + packobj = PyBytes_FromStringAndSize(NULL, nbytes); if (packobj == NULL) goto bail; - p = PyString_AS_STRING(packobj); + p = PyBytes_AS_STRING(packobj); pn = PySequence_ITEM(pl, 0); - if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) { + if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) { PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash"); goto bail; } memcpy(p, s, l); p += 20; pn = PySequence_ITEM(pl, 1); - if (PyString_AsStringAndSize(pn, &s, &l) == -1 || l != 20) { + if (PyBytes_AsStringAndSize(pn, &s, &l) == -1 || l != 20) { PyErr_SetString(PyExc_TypeError, "expected a 20-byte hash"); goto bail; } @@ -685,21 +697,21 @@ putbe32((uint32_t)mtime, p + 8); t = p + 12; p += 16; - len = PyString_GET_SIZE(k); - memcpy(p, PyString_AS_STRING(k), len); + len = PyBytes_GET_SIZE(k); + memcpy(p, PyBytes_AS_STRING(k), len); p += len; o = PyDict_GetItem(copymap, k); if (o) { *p++ = '\0'; - l = PyString_GET_SIZE(o); - memcpy(p, PyString_AS_STRING(o), l); + l = PyBytes_GET_SIZE(o); + memcpy(p, PyBytes_AS_STRING(o), l); p += l; len += l + 1; } putbe32((uint32_t)len, t); } - pos = p - PyString_AS_STRING(packobj); + pos = p - PyBytes_AS_STRING(packobj); if (pos != nbytes) { PyErr_Format(PyExc_SystemError, "bad dirstate size: %ld != %ld", (long)pos, (long)nbytes); @@ -796,7 +808,7 @@ return self->offsets[pos]; } - return PyString_AS_STRING(self->data) + pos * v1_hdrsize; + return PyBytes_AS_STRING(self->data) + pos * v1_hdrsize; } static inline int index_get_parents(indexObject *self, Py_ssize_t rev, @@ -926,7 +938,7 @@ PyObject *tuple, *str; tuple = PyList_GET_ITEM(self->added, pos - self->length + 1); str = PyTuple_GetItem(tuple, 7); - return str ? PyString_AS_STRING(str) : NULL; + return str ? PyBytes_AS_STRING(str) : NULL; } data = index_deref(self, pos); @@ -937,7 +949,7 @@ static int node_check(PyObject *obj, char **node, Py_ssize_t *nodelen) { - if (PyString_AsStringAndSize(obj, node, nodelen) == -1) + if (PyBytes_AsStringAndSize(obj, node, nodelen) == -1) return -1; if (*nodelen == 20) return 0; @@ -1825,7 +1837,7 @@ case -2: Py_RETURN_NONE; case -1: - return PyString_FromStringAndSize(nullid, 20); + return PyBytes_FromStringAndSize(nullid, 20); } fullnode = index_node(self, rev); @@ -1834,7 +1846,7 @@ "could not access rev %d", rev); return NULL; } - return PyString_FromStringAndSize(fullnode, 20); + return PyBytes_FromStringAndSize(fullnode, 20); } static PyObject *index_m_get(indexObject *self, PyObject *args) @@ -2247,7 +2259,7 @@ PyObject *tuple = PyList_GET_ITEM(self->added, i); PyObject *node = PyTuple_GET_ITEM(tuple, 7); - nt_insert(self, PyString_AS_STRING(node), -1); + nt_insert(self, PyBytes_AS_STRING(node), -1); } if (start == 0) @@ -2264,7 +2276,12 @@ Py_ssize_t length = index_length(self); int ret = 0; +/* Argument changed from PySliceObject* to PyObject* in Python 3. */ +#ifdef IS_PY3K + if (PySlice_GetIndicesEx(item, length, +#else if (PySlice_GetIndicesEx((PySliceObject*)item, length, +#endif &start, &stop, &step, &slicelength) < 0) return -1; @@ -2372,9 +2389,9 @@ */ static Py_ssize_t inline_scan(indexObject *self, const char **offsets) { - const char *data = PyString_AS_STRING(self->data); + const char *data = PyBytes_AS_STRING(self->data); Py_ssize_t pos = 0; - Py_ssize_t end = PyString_GET_SIZE(self->data); + Py_ssize_t end = PyBytes_GET_SIZE(self->data); long incr = v1_hdrsize; Py_ssize_t len = 0; @@ -2416,11 +2433,11 @@ if (!PyArg_ParseTuple(args, "OO", &data_obj, &inlined_obj)) return -1; - if (!PyString_Check(data_obj)) { + if (!PyBytes_Check(data_obj)) { PyErr_SetString(PyExc_TypeError, "data is not a string"); return -1; } - size = PyString_GET_SIZE(data_obj); + size = PyBytes_GET_SIZE(data_obj); self->inlined = inlined_obj && PyObject_IsTrue(inlined_obj); self->data = data_obj; @@ -2516,8 +2533,7 @@ }; static PyTypeObject indexType = { - PyObject_HEAD_INIT(NULL) - 0, /* ob_size */ + PyVarObject_HEAD_INIT(NULL, 0) "parsers.index", /* tp_name */ sizeof(indexObject), /* tp_basicsize */ 0, /* tp_itemsize */ @@ -2613,7 +2629,7 @@ return NULL; } for (i = 0; i < num; i++) { - PyObject *hash = PyString_FromStringAndSize(source, hashwidth); + PyObject *hash = PyBytes_FromStringAndSize(source, hashwidth); if (hash == NULL) { Py_DECREF(list); return NULL; @@ -2669,7 +2685,7 @@ if (data + hashwidth > dataend) { goto overflow; } - prec = PyString_FromStringAndSize(data, hashwidth); + prec = PyBytes_FromStringAndSize(data, hashwidth); data += hashwidth; if (prec == NULL) { goto bail; @@ -2712,9 +2728,9 @@ if (meta + leftsize + rightsize > dataend) { goto overflow; } - left = PyString_FromStringAndSize(meta, leftsize); + left = PyBytes_FromStringAndSize(meta, leftsize); meta += leftsize; - right = PyString_FromStringAndSize(meta, rightsize); + right = PyBytes_FromStringAndSize(meta, rightsize); meta += rightsize; tmp = PyTuple_New(2); if (!left || !right || !tmp) { @@ -2880,7 +2896,7 @@ PyObject *mod; if (check_python_version() == -1) - return; + return NULL; mod = PyModule_Create(&parsers_module); module_init(mod); return mod;
--- a/mercurial/patch.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/patch.py Tue Oct 18 14:15:15 2016 -0500 @@ -410,11 +410,7 @@ return self.fp.readline() def __iter__(self): - while True: - l = self.readline() - if not l: - break - yield l + return iter(self.readline, '') class abstractbackend(object): def __init__(self, ui): @@ -673,6 +669,8 @@ self.mode = (False, False) if self.missing: self.ui.warn(_("unable to find '%s' for patching\n") % self.fname) + self.ui.warn(_("(use '--prefix' to apply patch relative to the " + "current directory)\n")) self.hash = {} self.dirty = 0 @@ -1688,10 +1686,7 @@ def scanwhile(first, p): """scan lr while predicate holds""" lines = [first] - while True: - line = lr.readline() - if not line: - break + for line in iter(lr.readline, ''): if p(line): lines.append(line) else: @@ -1699,10 +1694,7 @@ break return lines - while True: - line = lr.readline() - if not line: - break + for line in iter(lr.readline, ''): if line.startswith('diff --git a/') or line.startswith('diff -r '): def notheader(line): s = line.split(None, 1) @@ -1772,10 +1764,7 @@ context = None lr = linereader(fp) - while True: - x = lr.readline() - if not x: - break + for x in iter(lr.readline, ''): if state == BFILE and ( (not context and x[0] == '@') or (context is not False and x.startswith('***************')) @@ -1963,8 +1952,10 @@ data, mode = None, None if gp.op in ('RENAME', 'COPY'): data, mode = store.getfile(gp.oldpath)[:2] - # FIXME: failing getfile has never been handled here - assert data is not None + if data is None: + # This means that the old path does not exist + raise PatchError(_("source file '%s' does not exist") + % gp.oldpath) if gp.mode: mode = gp.mode if gp.op == 'ADD': @@ -2155,7 +2146,14 @@ def get(key, name=None, getter=ui.configbool, forceplain=None): if opts: v = opts.get(key) - if v: + # diffopts flags are either None-default (which is passed + # through unchanged, so we can identify unset values), or + # some other falsey default (eg --unified, which defaults + # to an empty string). We only want to override the config + # entries from hgrc with command line values if they + # appear to have been set, which is any truthy value, + # True, or False. + if v or isinstance(v, bool): return v if forceplain is not None and ui.plain(): return forceplain
--- a/mercurial/pathencode.c Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/pathencode.c Tue Oct 18 14:15:15 2016 -0500 @@ -156,7 +156,7 @@ if (!PyArg_ParseTuple(args, "O:encodedir", &pathobj)) return NULL; - if (PyString_AsStringAndSize(pathobj, &path, &len) == -1) { + if (PyBytes_AsStringAndSize(pathobj, &path, &len) == -1) { PyErr_SetString(PyExc_TypeError, "expected a string"); return NULL; } @@ -168,11 +168,12 @@ return pathobj; } - newobj = PyString_FromStringAndSize(NULL, newlen); + newobj = PyBytes_FromStringAndSize(NULL, newlen); if (newobj) { - PyString_GET_SIZE(newobj)--; - _encodedir(PyString_AS_STRING(newobj), newlen, path, + assert(PyBytes_Check(newobj)); + Py_SIZE(newobj)--; + _encodedir(PyBytes_AS_STRING(newobj), newlen, path, len + 1); } @@ -515,9 +516,9 @@ return NULL; newlen = _lowerencode(NULL, 0, path, len); - ret = PyString_FromStringAndSize(NULL, newlen); + ret = PyBytes_FromStringAndSize(NULL, newlen); if (ret) - _lowerencode(PyString_AS_STRING(ret), newlen, path, len); + _lowerencode(PyBytes_AS_STRING(ret), newlen, path, len); return ret; } @@ -568,11 +569,11 @@ if (lastdot >= 0) destsize += len - lastdot - 1; - ret = PyString_FromStringAndSize(NULL, destsize); + ret = PyBytes_FromStringAndSize(NULL, destsize); if (ret == NULL) return NULL; - dest = PyString_AS_STRING(ret); + dest = PyBytes_AS_STRING(ret); memcopy(dest, &destlen, destsize, "dh/", 3); /* Copy up to dirprefixlen bytes of each path component, up to @@ -638,7 +639,8 @@ memcopy(dest, &destlen, destsize, &src[lastdot], len - lastdot - 1); - PyString_GET_SIZE(ret) = destlen; + assert(PyBytes_Check(ret)); + Py_SIZE(ret) = destlen; return ret; } @@ -653,7 +655,7 @@ PyObject *shaobj, *hashobj; if (shafunc == NULL) { - PyObject *hashlib, *name = PyString_FromString("hashlib"); + PyObject *hashlib, *name = PyBytes_FromString("hashlib"); if (name == NULL) return -1; @@ -686,14 +688,14 @@ if (hashobj == NULL) return -1; - if (!PyString_Check(hashobj) || PyString_GET_SIZE(hashobj) != 20) { + if (!PyBytes_Check(hashobj) || PyBytes_GET_SIZE(hashobj) != 20) { PyErr_SetString(PyExc_TypeError, "result of digest is not a 20-byte hash"); Py_DECREF(hashobj); return -1; } - memcpy(hash, PyString_AS_STRING(hashobj), 20); + memcpy(hash, PyBytes_AS_STRING(hashobj), 20); Py_DECREF(hashobj); return 0; } @@ -731,7 +733,7 @@ if (!PyArg_ParseTuple(args, "O:pathencode", &pathobj)) return NULL; - if (PyString_AsStringAndSize(pathobj, &path, &len) == -1) { + if (PyBytes_AsStringAndSize(pathobj, &path, &len) == -1) { PyErr_SetString(PyExc_TypeError, "expected a string"); return NULL; } @@ -747,11 +749,12 @@ return pathobj; } - newobj = PyString_FromStringAndSize(NULL, newlen); + newobj = PyBytes_FromStringAndSize(NULL, newlen); if (newobj) { - PyString_GET_SIZE(newobj)--; - basicencode(PyString_AS_STRING(newobj), newlen, path, + assert(PyBytes_Check(newobj)); + Py_SIZE(newobj)--; + basicencode(PyBytes_AS_STRING(newobj), newlen, path, len + 1); } }
--- a/mercurial/pathutil.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/pathutil.py Tue Oct 18 14:15:15 2016 -0500 @@ -40,7 +40,7 @@ self.root = root self._realfs = realfs self.callback = callback - if os.path.lexists(root) and not util.checkcase(root): + if os.path.lexists(root) and not util.fscasesensitive(root): self.normcase = util.normcase else: self.normcase = lambda x: x
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/profiling.py Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,164 @@ +# profiling.py - profiling functions +# +# Copyright 2016 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, print_function + +import contextlib +import os +import sys +import time + +from .i18n import _ +from . import ( + error, + util, +) + +@contextlib.contextmanager +def lsprofile(ui, fp): + format = ui.config('profiling', 'format', default='text') + field = ui.config('profiling', 'sort', default='inlinetime') + limit = ui.configint('profiling', 'limit', default=30) + climit = ui.configint('profiling', 'nested', default=0) + + if format not in ['text', 'kcachegrind']: + ui.warn(_("unrecognized profiling format '%s'" + " - Ignored\n") % format) + format = 'text' + + try: + from . import lsprof + except ImportError: + raise error.Abort(_( + 'lsprof not available - install from ' + 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/')) + p = lsprof.Profiler() + p.enable(subcalls=True) + try: + yield + finally: + p.disable() + + if format == 'kcachegrind': + from . import lsprofcalltree + calltree = lsprofcalltree.KCacheGrind(p) + calltree.output(fp) + else: + # format == 'text' + stats = lsprof.Stats(p.getstats()) + stats.sort(field) + stats.pprint(limit=limit, file=fp, climit=climit) + +@contextlib.contextmanager +def flameprofile(ui, fp): + try: + from flamegraph import flamegraph + except ImportError: + raise error.Abort(_( + 'flamegraph not available - install from ' + 'https://github.com/evanhempel/python-flamegraph')) + # developer config: profiling.freq + freq = ui.configint('profiling', 'freq', default=1000) + filter_ = None + collapse_recursion = True + thread = flamegraph.ProfileThread(fp, 1.0 / freq, + filter_, collapse_recursion) + start_time = time.clock() + try: + thread.start() + yield + finally: + thread.stop() + thread.join() + print('Collected %d stack frames (%d unique) in %2.2f seconds.' % ( + time.clock() - start_time, thread.num_frames(), + thread.num_frames(unique=True))) + +@contextlib.contextmanager +def statprofile(ui, fp): + try: + import statprof + except ImportError: + raise error.Abort(_( + 'statprof not available - install using "easy_install statprof"')) + + freq = ui.configint('profiling', 'freq', default=1000) + if freq > 0: + # Cannot reset when profiler is already active. So silently no-op. + if statprof.state.profile_level == 0: + statprof.reset(freq) + else: + ui.warn(_("invalid sampling frequency '%s' - ignoring\n") % freq) + + statprof.start() + try: + yield + finally: + statprof.stop() + statprof.display(fp) + +@contextlib.contextmanager +def profile(ui): + """Start profiling. + + Profiling is active when the context manager is active. When the context + manager exits, profiling results will be written to the configured output. + """ + profiler = os.getenv('HGPROF') + if profiler is None: + profiler = ui.config('profiling', 'type', default='ls') + if profiler not in ('ls', 'stat', 'flame'): + ui.warn(_("unrecognized profiler '%s' - ignored\n") % profiler) + profiler = 'ls' + + output = ui.config('profiling', 'output') + + if output == 'blackbox': + fp = util.stringio() + elif output: + path = ui.expandpath(output) + fp = open(path, 'wb') + else: + fp = sys.stderr + + try: + if profiler == 'ls': + proffn = lsprofile + elif profiler == 'flame': + proffn = flameprofile + else: + proffn = statprofile + + with proffn(ui, fp): + yield + + finally: + if output: + if output == 'blackbox': + val = 'Profile:\n%s' % fp.getvalue() + # ui.log treats the input as a format string, + # so we need to escape any % signs. + val = val.replace('%', '%%') + ui.log('profile', val) + fp.close() + +@contextlib.contextmanager +def maybeprofile(ui): + """Profile if enabled, else do nothing. + + This context manager can be used to optionally profile if profiling + is enabled. Otherwise, it does nothing. + + The purpose of this context manager is to make calling code simpler: + just use a single code path for calling into code you may want to profile + and this function determines whether to start profiling. + """ + if ui.configbool('profiling', 'enabled'): + with profile(ui): + yield + else: + yield
--- a/mercurial/pure/bdiff.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/pure/bdiff.py Tue Oct 18 14:15:15 2016 -0500 @@ -12,6 +12,10 @@ import re import struct +from . import policy +policynocffi = policy.policynocffi +modulepolicy = policy.policy + def splitnewlines(text): '''like str.splitlines, but only split on newlines.''' lines = [l + '\n' for l in text.split('\n')] @@ -96,3 +100,70 @@ text = re.sub('[ \t\r]+', ' ', text) text = text.replace(' \n', '\n') return text + +if modulepolicy not in policynocffi: + try: + from _bdiff_cffi import ffi, lib + except ImportError: + if modulepolicy == 'cffi': # strict cffi import + raise + else: + def blocks(sa, sb): + a = ffi.new("struct bdiff_line**") + b = ffi.new("struct bdiff_line**") + ac = ffi.new("char[]", str(sa)) + bc = ffi.new("char[]", str(sb)) + l = ffi.new("struct bdiff_hunk*") + try: + an = lib.bdiff_splitlines(ac, len(sa), a) + bn = lib.bdiff_splitlines(bc, len(sb), b) + if not a[0] or not b[0]: + raise MemoryError + count = lib.bdiff_diff(a[0], an, b[0], bn, l) + if count < 0: + raise MemoryError + rl = [None] * count + h = l.next + i = 0 + while h: + rl[i] = (h.a1, h.a2, h.b1, h.b2) + h = h.next + i += 1 + finally: + lib.free(a[0]) + lib.free(b[0]) + lib.bdiff_freehunks(l.next) + return rl + + def bdiff(sa, sb): + a = ffi.new("struct bdiff_line**") + b = ffi.new("struct bdiff_line**") + ac = ffi.new("char[]", str(sa)) + bc = ffi.new("char[]", str(sb)) + l = ffi.new("struct bdiff_hunk*") + try: + an = lib.bdiff_splitlines(ac, len(sa), a) + bn = lib.bdiff_splitlines(bc, len(sb), b) + if not a[0] or not b[0]: + raise MemoryError + count = lib.bdiff_diff(a[0], an, b[0], bn, l) + if count < 0: + raise MemoryError + rl = [] + h = l.next + la = lb = 0 + while h: + if h.a1 != la or h.b1 != lb: + lgt = (b[0] + h.b1).l - (b[0] + lb).l + rl.append(struct.pack(">lll", (a[0] + la).l - a[0].l, + (a[0] + h.a1).l - a[0].l, lgt)) + rl.append(str(ffi.buffer((b[0] + lb).l, lgt))) + la = h.a2 + lb = h.b2 + h = h.next + + finally: + lib.free(a[0]) + lib.free(b[0]) + lib.bdiff_freehunks(l.next) + return "".join(rl)
--- a/mercurial/pure/mpatch.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/pure/mpatch.py Tue Oct 18 14:15:15 2016 -0500 @@ -9,8 +9,10 @@ import struct -from . import pycompat +from . import policy, pycompat stringio = pycompat.stringio +modulepolicy = policy.policy +policynocffi = policy.policynocffi class mpatchError(Exception): """error raised when a delta cannot be decoded @@ -125,3 +127,44 @@ outlen += orig - last return outlen + +if modulepolicy not in policynocffi: + try: + from _mpatch_cffi import ffi, lib + except ImportError: + if modulepolicy == 'cffi': # strict cffi import + raise + else: + @ffi.def_extern() + def cffi_get_next_item(arg, pos): + all, bins = ffi.from_handle(arg) + container = ffi.new("struct mpatch_flist*[1]") + to_pass = ffi.new("char[]", str(bins[pos])) + all.append(to_pass) + r = lib.mpatch_decode(to_pass, len(to_pass) - 1, container) + if r < 0: + return ffi.NULL + return container[0] + + def patches(text, bins): + lgt = len(bins) + all = [] + if not lgt: + return text + arg = (all, bins) + patch = lib.mpatch_fold(ffi.new_handle(arg), + lib.cffi_get_next_item, 0, lgt) + if not patch: + raise mpatchError("cannot decode chunk") + outlen = lib.mpatch_calcsize(len(text), patch) + if outlen < 0: + lib.mpatch_lfree(patch) + raise mpatchError("inconsistency detected") + buf = ffi.new("char[]", outlen) + if lib.mpatch_apply(buf, text, len(text), patch) < 0: + lib.mpatch_lfree(patch) + raise mpatchError("error applying patches") + res = ffi.buffer(buf, outlen)[:] + lib.mpatch_lfree(patch) + return res +
--- a/mercurial/pure/osutil.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/pure/osutil.py Tue Oct 18 14:15:15 2016 -0500 @@ -120,13 +120,14 @@ if skip == name and tp == statmod.S_ISDIR: return [] if stat: - mtime = cur.time.tv_sec + mtime = cur.mtime.tv_sec mode = (cur.accessmask & ~lib.S_IFMT)| tp ret.append((name, tp, stat_res(st_mode=mode, st_mtime=mtime, st_size=cur.datalength))) else: ret.append((name, tp)) - cur += lgt + cur = ffi.cast("val_attrs_t*", int(ffi.cast("intptr_t", cur)) + + lgt) return ret def listdir(path, stat=False, skip=None): @@ -173,30 +174,30 @@ class _iovec(ctypes.Structure): _fields_ = [ - ('iov_base', ctypes.c_void_p), - ('iov_len', ctypes.c_size_t), + (u'iov_base', ctypes.c_void_p), + (u'iov_len', ctypes.c_size_t), ] class _msghdr(ctypes.Structure): _fields_ = [ - ('msg_name', ctypes.c_void_p), - ('msg_namelen', _socklen_t), - ('msg_iov', ctypes.POINTER(_iovec)), - ('msg_iovlen', _msg_iovlen_t), - ('msg_control', ctypes.c_void_p), - ('msg_controllen', _msg_controllen_t), - ('msg_flags', ctypes.c_int), + (u'msg_name', ctypes.c_void_p), + (u'msg_namelen', _socklen_t), + (u'msg_iov', ctypes.POINTER(_iovec)), + (u'msg_iovlen', _msg_iovlen_t), + (u'msg_control', ctypes.c_void_p), + (u'msg_controllen', _msg_controllen_t), + (u'msg_flags', ctypes.c_int), ] class _cmsghdr(ctypes.Structure): _fields_ = [ - ('cmsg_len', _cmsg_len_t), - ('cmsg_level', ctypes.c_int), - ('cmsg_type', ctypes.c_int), - ('cmsg_data', ctypes.c_ubyte * 0), + (u'cmsg_len', _cmsg_len_t), + (u'cmsg_level', ctypes.c_int), + (u'cmsg_type', ctypes.c_int), + (u'cmsg_data', ctypes.c_ubyte * 0), ] - _libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True) + _libc = ctypes.CDLL(ctypes.util.find_library(u'c'), use_errno=True) _recvmsg = getattr(_libc, 'recvmsg', None) if _recvmsg: _recvmsg.restype = getattr(ctypes, 'c_ssize_t', ctypes.c_long)
--- a/mercurial/pycompat.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/pycompat.py Tue Oct 18 14:15:15 2016 -0500 @@ -12,7 +12,9 @@ import sys -if sys.version_info[0] < 3: +ispy3 = (sys.version_info[0] >= 3) + +if not ispy3: import cPickle as pickle import cStringIO as io import httplib @@ -29,36 +31,84 @@ import urllib.parse as urlparse import xmlrpc.client as xmlrpclib +if ispy3: + import builtins + import functools + import os + fsencode = os.fsencode + + def sysstr(s): + """Return a keyword str to be passed to Python functions such as + getattr() and str.encode() + + This never raises UnicodeDecodeError. Non-ascii characters are + considered invalid and mapped to arbitrary but unique code points + such that 'sysstr(a) != sysstr(b)' for all 'a != b'. + """ + if isinstance(s, builtins.str): + return s + return s.decode(u'latin-1') + + def _wrapattrfunc(f): + @functools.wraps(f) + def w(object, name, *args): + return f(object, sysstr(name), *args) + return w + + # these wrappers are automagically imported by hgloader + delattr = _wrapattrfunc(builtins.delattr) + getattr = _wrapattrfunc(builtins.getattr) + hasattr = _wrapattrfunc(builtins.hasattr) + setattr = _wrapattrfunc(builtins.setattr) + xrange = builtins.range + +else: + def sysstr(s): + return s + + # Partial backport from os.py in Python 3, which only accepts bytes. + # In Python 2, our paths should only ever be bytes, a unicode path + # indicates a bug. + def fsencode(filename): + if isinstance(filename, str): + return filename + else: + raise TypeError( + "expect str, not %s" % type(filename).__name__) + stringio = io.StringIO empty = _queue.Empty queue = _queue.Queue class _pycompatstub(object): - pass - -def _alias(alias, origin, items): - """ populate a _pycompatstub + def __init__(self): + self._aliases = {} - copies items from origin to alias - """ - def hgcase(item): - return item.replace('_', '').lower() - for item in items: + def _registeraliases(self, origin, items): + """Add items that will be populated at the first access""" + items = map(sysstr, items) + self._aliases.update( + (item.replace(sysstr('_'), sysstr('')).lower(), (origin, item)) + for item in items) + + def __getattr__(self, name): try: - setattr(alias, hgcase(item), getattr(origin, item)) - except AttributeError: - pass + origin, item = self._aliases[name] + except KeyError: + raise AttributeError(name) + self.__dict__[name] = obj = getattr(origin, item) + return obj httpserver = _pycompatstub() urlreq = _pycompatstub() urlerr = _pycompatstub() -try: +if not ispy3: import BaseHTTPServer import CGIHTTPServer import SimpleHTTPServer import urllib2 import urllib - _alias(urlreq, urllib, ( + urlreq._registeraliases(urllib, ( "addclosehook", "addinfourl", "ftpwrapper", @@ -71,9 +121,8 @@ "unquote", "url2pathname", "urlencode", - "urlencode", )) - _alias(urlreq, urllib2, ( + urlreq._registeraliases(urllib2, ( "AbstractHTTPHandler", "BaseHandler", "build_opener", @@ -89,24 +138,24 @@ "Request", "urlopen", )) - _alias(urlerr, urllib2, ( + urlerr._registeraliases(urllib2, ( "HTTPError", "URLError", )) - _alias(httpserver, BaseHTTPServer, ( + httpserver._registeraliases(BaseHTTPServer, ( "HTTPServer", "BaseHTTPRequestHandler", )) - _alias(httpserver, SimpleHTTPServer, ( + httpserver._registeraliases(SimpleHTTPServer, ( "SimpleHTTPRequestHandler", )) - _alias(httpserver, CGIHTTPServer, ( + httpserver._registeraliases(CGIHTTPServer, ( "CGIHTTPRequestHandler", )) -except ImportError: +else: import urllib.request - _alias(urlreq, urllib.request, ( + urlreq._registeraliases(urllib.request, ( "AbstractHTTPHandler", "addclosehook", "addinfourl", @@ -134,20 +183,14 @@ "urlopen", )) import urllib.error - _alias(urlerr, urllib.error, ( + urlerr._registeraliases(urllib.error, ( "HTTPError", "URLError", )) import http.server - _alias(httpserver, http.server, ( + httpserver._registeraliases(http.server, ( "HTTPServer", "BaseHTTPRequestHandler", "SimpleHTTPRequestHandler", "CGIHTTPRequestHandler", )) - -try: - xrange -except NameError: - import builtins - builtins.xrange = range
--- a/mercurial/registrar.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/registrar.py Tue Oct 18 14:15:15 2016 -0500 @@ -8,6 +8,7 @@ from __future__ import absolute_import from . import ( + pycompat, util, ) @@ -108,6 +109,9 @@ Optional argument 'safe' indicates whether a predicate is safe for DoS attack (False by default). + Optional argument 'takeorder' indicates whether a predicate function + takes ordering policy as the last argument. + 'revsetpredicate' instance in example above can be used to decorate multiple functions. @@ -118,10 +122,11 @@ Otherwise, explicit 'revset.loadpredicate()' is needed. """ _getname = _funcregistrarbase._parsefuncdecl - _docformat = "``%s``\n %s" + _docformat = pycompat.sysstr("``%s``\n %s") - def _extrasetup(self, name, func, safe=False): + def _extrasetup(self, name, func, safe=False, takeorder=False): func._safe = safe + func._takeorder = takeorder class filesetpredicate(_funcregistrarbase): """Decorator to register fileset predicate @@ -156,7 +161,7 @@ Otherwise, explicit 'fileset.loadpredicate()' is needed. """ _getname = _funcregistrarbase._parsefuncdecl - _docformat = "``%s``\n %s" + _docformat = pycompat.sysstr("``%s``\n %s") def _extrasetup(self, name, func, callstatus=False, callexisting=False): func._callstatus = callstatus @@ -165,7 +170,7 @@ class _templateregistrarbase(_funcregistrarbase): """Base of decorator to register functions as template specific one """ - _docformat = ":%s: %s" + _docformat = pycompat.sysstr(":%s: %s") class templatekeyword(_templateregistrarbase): """Decorator to register template keyword
--- a/mercurial/repair.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/repair.py Tue Oct 18 14:15:15 2016 -0500 @@ -147,9 +147,10 @@ vfs.join(backupfile)) repo.ui.log("backupbundle", "saved backup bundle to %s\n", vfs.join(backupfile)) - if saveheads or savebases: - # do not compress partial bundle if we remove it from disk later - chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp', + tmpbundlefile = None + if saveheads: + # do not compress temporary bundle if we remove it from disk later + tmpbundlefile = _bundle(repo, savebases, saveheads, node, 'temp', compress=False) mfst = repo.manifest @@ -173,32 +174,34 @@ if (unencoded.startswith('meta/') and unencoded.endswith('00manifest.i')): dir = unencoded[5:-12] - repo.dirlog(dir).strip(striprev, tr) + repo.manifest.dirlog(dir).strip(striprev, tr) for fn in files: repo.file(fn).strip(striprev, tr) tr.endgroup() for i in xrange(offset, len(tr.entries)): file, troffset, ignore = tr.entries[i] - repo.svfs(file, 'a').truncate(troffset) + with repo.svfs(file, 'a', checkambig=True) as fp: + fp.truncate(troffset) if troffset == 0: repo.store.markremoved(file) - if saveheads or savebases: + if tmpbundlefile: ui.note(_("adding branch\n")) - f = vfs.open(chgrpfile, "rb") - gen = exchange.readbundle(ui, f, chgrpfile, vfs) + f = vfs.open(tmpbundlefile, "rb") + gen = exchange.readbundle(ui, f, tmpbundlefile, vfs) if not repo.ui.verbose: # silence internal shuffling chatter repo.ui.pushbuffer() if isinstance(gen, bundle2.unbundle20): with repo.transaction('strip') as tr: tr.hookargs = {'source': 'strip', - 'url': 'bundle:' + vfs.join(chgrpfile)} + 'url': 'bundle:' + vfs.join(tmpbundlefile)} bundle2.applybundle(repo, gen, tr, source='strip', - url='bundle:' + vfs.join(chgrpfile)) + url='bundle:' + vfs.join(tmpbundlefile)) else: - gen.apply(repo, 'strip', 'bundle:' + vfs.join(chgrpfile), True) + gen.apply(repo, 'strip', 'bundle:' + vfs.join(tmpbundlefile), + True) if not repo.ui.verbose: repo.ui.popbuffer() f.close() @@ -227,16 +230,18 @@ except: # re-raises if backupfile: - ui.warn(_("strip failed, full bundle stored in '%s'\n") + ui.warn(_("strip failed, backup bundle stored in '%s'\n") % vfs.join(backupfile)) - elif saveheads: - ui.warn(_("strip failed, partial bundle stored in '%s'\n") - % vfs.join(chgrpfile)) + if tmpbundlefile: + ui.warn(_("strip failed, unrecovered changes stored in '%s'\n") + % vfs.join(tmpbundlefile)) + ui.warn(_("(fix the problem, then recover the changesets with " + "\"hg unbundle '%s'\")\n") % vfs.join(tmpbundlefile)) raise else: - if saveheads or savebases: - # Remove partial backup only if there were no exceptions - vfs.unlink(chgrpfile) + if tmpbundlefile: + # Remove temporary bundle only if there were no exceptions + vfs.unlink(tmpbundlefile) repo.destroyed()
--- a/mercurial/revlog.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/revlog.py Tue Oct 18 14:15:15 2016 -0500 @@ -212,8 +212,11 @@ fashion, which means we never need to rewrite a file to insert or remove data, and can use some simple techniques to avoid the need for locking while reading. + + If checkambig, indexfile is opened with checkambig=True at + writing, to avoid file stat ambiguity. """ - def __init__(self, opener, indexfile): + def __init__(self, opener, indexfile, checkambig=False): """ create a revlog object @@ -223,11 +226,13 @@ self.indexfile = indexfile self.datafile = indexfile[:-2] + ".d" self.opener = opener + # When True, indexfile is opened with checkambig=True at writing, to + # avoid file stat ambiguity. + self._checkambig = checkambig # 3-tuple of (node, rev, text) for a raw revision. self._cache = None - # 2-tuple of (rev, baserev) defining the base revision the delta chain - # begins at for a revision. - self._basecache = None + # Maps rev to chain base rev. + self._chainbasecache = util.lrucachedict(100) # 2-tuple of (offset, data) of raw data from the revlog at an offset. self._chunkcache = (0, '') # How much data to read and cache into the raw revlog data cache. @@ -292,6 +297,8 @@ raise RevlogError(_("index %s unknown format %d") % (self.indexfile, fmt)) + self.storedeltachains = True + self._io = revlogio() if self.version == REVLOGV0: self._io = revlogoldio() @@ -340,7 +347,7 @@ def clearcaches(self): self._cache = None - self._basecache = None + self._chainbasecache.clear() self._chunkcache = (0, '') self._pcache = {} @@ -390,11 +397,17 @@ def length(self, rev): return self.index[rev][1] def chainbase(self, rev): + base = self._chainbasecache.get(rev) + if base is not None: + return base + index = self.index base = index[rev][3] while base != rev: rev = base base = index[rev][3] + + self._chainbasecache[rev] = base return base def chainlen(self, rev): return self._chaininfo(rev)[0] @@ -1271,7 +1284,8 @@ finally: df.close() - fp = self.opener(self.indexfile, 'w', atomictemp=True) + fp = self.opener(self.indexfile, 'w', atomictemp=True, + checkambig=self._checkambig) self.version &= ~(REVLOGNGINLINEDATA) self._inline = False for i in self: @@ -1314,7 +1328,7 @@ dfh = None if not self._inline: dfh = self.opener(self.datafile, "a+") - ifh = self.opener(self.indexfile, "a+") + ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig) try: return self._addrevision(node, text, transaction, link, p1, p2, REVIDX_DEFAULT_FLAGS, cachedelta, ifh, dfh) @@ -1428,30 +1442,24 @@ fh = dfh ptext = self.revision(self.node(rev), _df=fh) delta = mdiff.textdiff(ptext, t) - data = self.compress(delta) - l = len(data[1]) + len(data[0]) - if basecache[0] == rev: - chainbase = basecache[1] - else: - chainbase = self.chainbase(rev) - dist = l + offset - self.start(chainbase) + header, data = self.compress(delta) + deltalen = len(header) + len(data) + chainbase = self.chainbase(rev) + dist = deltalen + offset - self.start(chainbase) if self._generaldelta: base = rev else: base = chainbase chainlen, compresseddeltalen = self._chaininfo(rev) chainlen += 1 - compresseddeltalen += l - return dist, l, data, base, chainbase, chainlen, compresseddeltalen + compresseddeltalen += deltalen + return (dist, deltalen, (header, data), base, + chainbase, chainlen, compresseddeltalen) curr = len(self) prev = curr - 1 - base = chainbase = curr offset = self.end(prev) delta = None - if self._basecache is None: - self._basecache = (prev, self.chainbase(prev)) - basecache = self._basecache p1r, p2r = self.rev(p1), self.rev(p2) # full versions are inserted when the needed deltas @@ -1463,8 +1471,12 @@ textlen = len(text) # should we try to build a delta? - if prev != nullrev: + if prev != nullrev and self.storedeltachains: tested = set() + # This condition is true most of the time when processing + # changegroup data into a generaldelta repo. The only time it + # isn't true is if this is the first revision in a delta chain + # or if ``format.generaldelta=true`` disabled ``lazydeltabase``. if cachedelta and self._generaldelta and self._lazydeltabase: # Assume what we received from the server is a good choice # build delta will reuse the cache @@ -1515,7 +1527,7 @@ if type(text) == str: # only accept immutable objects self._cache = (node, curr, text) - self._basecache = (curr, chainbase) + self._chainbasecache[curr] = chainbase return node def _writeentry(self, transaction, ifh, dfh, entry, data, link, offset): @@ -1569,7 +1581,7 @@ end = 0 if r: end = self.end(r - 1) - ifh = self.opener(self.indexfile, "a+") + ifh = self.opener(self.indexfile, "a+", checkambig=self._checkambig) isize = r * self._io.size if self._inline: transaction.add(self.indexfile, end + isize, r) @@ -1585,10 +1597,7 @@ try: # loop through our set of deltas chain = None - while True: - chunkdata = cg.deltachunk(chain) - if not chunkdata: - break + for chunkdata in iter(lambda: cg.deltachunk(chain), {}): node = chunkdata['node'] p1 = chunkdata['p1'] p2 = chunkdata['p2'] @@ -1646,7 +1655,8 @@ # reopen the index ifh.close() dfh = self.opener(self.datafile, "a+") - ifh = self.opener(self.indexfile, "a+") + ifh = self.opener(self.indexfile, "a+", + checkambig=self._checkambig) finally: if dfh: dfh.close()
--- a/mercurial/revset.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/revset.py Tue Oct 18 14:15:15 2016 -0500 @@ -9,6 +9,7 @@ import heapq import re +import string from .i18n import _ from . import ( @@ -22,6 +23,7 @@ parser, pathutil, phases, + pycompat, registrar, repoview, util, @@ -149,18 +151,16 @@ "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None), "##": (20, None, None, ("_concat", 20), None), "~": (18, None, None, ("ancestor", 18), None), - "^": (18, None, None, ("parent", 18), ("parentpost", 18)), + "^": (18, None, None, ("parent", 18), "parentpost"), "-": (5, None, ("negate", 19), ("minus", 5), None), - "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), - ("dagrangepost", 17)), - "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), - ("dagrangepost", 17)), - ":": (15, "rangeall", ("rangepre", 15), ("range", 15), ("rangepost", 15)), + "::": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"), + "..": (17, None, ("dagrangepre", 17), ("dagrange", 17), "dagrangepost"), + ":": (15, "rangeall", ("rangepre", 15), ("range", 15), "rangepost"), "not": (10, None, ("not", 10), None, None), "!": (10, None, ("not", 10), None, None), "and": (5, None, None, ("and", 5), None), "&": (5, None, None, ("and", 5), None), - "%": (5, None, None, ("only", 5), ("onlypost", 5)), + "%": (5, None, None, ("only", 5), "onlypost"), "or": (4, None, None, ("or", 4), None), "|": (4, None, None, ("or", 4), None), "+": (4, None, None, ("or", 4), None), @@ -175,12 +175,12 @@ keywords = set(['and', 'or', 'not']) # default set of valid characters for the initial letter of symbols -_syminitletters = set(c for c in [chr(i) for i in xrange(256)] - if c.isalnum() or c in '._@' or ord(c) > 127) +_syminitletters = set( + string.ascii_letters + + string.digits + pycompat.sysstr('._@')) | set(map(chr, xrange(128, 256))) # default set of valid characters for non-initial letters of symbols -_symletters = set(c for c in [chr(i) for i in xrange(256)] - if c.isalnum() or c in '-._/@' or ord(c) > 127) +_symletters = _syminitletters | set(pycompat.sysstr('-/')) def tokenize(program, lookup=None, syminitletters=None, symletters=None): ''' @@ -362,14 +362,22 @@ return baseset([x]) return baseset() -def rangeset(repo, subset, x, y): +def rangeset(repo, subset, x, y, order): m = getset(repo, fullreposet(repo), x) n = getset(repo, fullreposet(repo), y) if not m or not n: return baseset() - m, n = m.first(), n.last() - + return _makerangeset(repo, subset, m.first(), n.last(), order) + +def rangepre(repo, subset, y, order): + # ':y' can't be rewritten to '0:y' since '0' may be hidden + n = getset(repo, fullreposet(repo), y) + if not n: + return baseset() + return _makerangeset(repo, subset, 0, n.last(), order) + +def _makerangeset(repo, subset, m, n, order): if m == n: r = baseset([m]) elif n == node.wdirrev: @@ -380,35 +388,43 @@ r = spanset(repo, m, n + 1) else: r = spanset(repo, m, n - 1) - # XXX We should combine with subset first: 'subset & baseset(...)'. This is - # necessary to ensure we preserve the order in subset. - # - # This has performance implication, carrying the sorting over when possible - # would be more efficient. - return r & subset - -def dagrange(repo, subset, x, y): + + if order == defineorder: + return r & subset + else: + # carrying the sorting over when possible would be more efficient + return subset & r + +def dagrange(repo, subset, x, y, order): r = fullreposet(repo) xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y), includepath=True) return subset & xs -def andset(repo, subset, x, y): +def andset(repo, subset, x, y, order): return getset(repo, getset(repo, subset, x), y) -def differenceset(repo, subset, x, y): +def differenceset(repo, subset, x, y, order): return getset(repo, subset, x) - getset(repo, subset, y) -def orset(repo, subset, *xs): +def _orsetlist(repo, subset, xs): assert xs if len(xs) == 1: return getset(repo, subset, xs[0]) p = len(xs) // 2 - a = orset(repo, subset, *xs[:p]) - b = orset(repo, subset, *xs[p:]) + a = _orsetlist(repo, subset, xs[:p]) + b = _orsetlist(repo, subset, xs[p:]) return a + b -def notset(repo, subset, x): +def orset(repo, subset, x, order): + xs = getlist(x) + if order == followorder: + # slow path to take the subset order + return subset & _orsetlist(repo, fullreposet(repo), xs) + else: + return _orsetlist(repo, subset, xs) + +def notset(repo, subset, x, order): return subset - getset(repo, subset, x) def listset(repo, subset, *xs): @@ -418,10 +434,13 @@ def keyvaluepair(repo, subset, k, v): raise error.ParseError(_("can't use a key-value pair in this context")) -def func(repo, subset, a, b): +def func(repo, subset, a, b, order): f = getsymbol(a) if f in symbols: - return symbols[f](repo, subset, b) + fn = symbols[f] + if getattr(fn, '_takeorder', False): + return fn(repo, subset, b, order) + return fn(repo, subset, b) keep = lambda fn: getattr(fn, '__doc__', None) is not None @@ -515,7 +534,7 @@ # Like ``ancestors(set)`` but follows only the first parents. return _ancestors(repo, subset, x, followfirst=True) -def ancestorspec(repo, subset, x, n): +def ancestorspec(repo, subset, x, n, order): """``set~n`` Changesets that are the Nth ancestor (first parents only) of a changeset in set. @@ -1001,12 +1020,21 @@ return limit(repo, subset, x) def _follow(repo, subset, x, name, followfirst=False): - l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name) + l = getargs(x, 0, 2, _("%s takes no arguments or a pattern " + "and an optional revset") % name) c = repo['.'] if l: x = getstring(l[0], _("%s expected a pattern") % name) + rev = None + if len(l) >= 2: + revs = getset(repo, fullreposet(repo), l[1]) + if len(revs) != 1: + raise error.RepoLookupError( + _("%s expected one starting revision") % name) + rev = revs.last() + c = repo[rev] matcher = matchmod.match(repo.root, repo.getcwd(), [x], - ctx=repo[None], default='path') + ctx=repo[rev], default='path') files = c.manifest().walk(matcher) @@ -1021,20 +1049,20 @@ return subset & s -@predicate('follow([pattern])', safe=True) +@predicate('follow([pattern[, startrev]])', safe=True) def follow(repo, subset, x): """ An alias for ``::.`` (ancestors of the working directory's first parent). If pattern is specified, the histories of files matching given - pattern is followed, including copies. + pattern in the revision given by startrev are followed, including copies. """ return _follow(repo, subset, x, 'follow') @predicate('_followfirst', safe=True) def _followfirst(repo, subset, x): - # ``followfirst([pattern])`` - # Like ``follow([pattern])`` but follows only the first parent of - # every revisions or files revisions. + # ``followfirst([pattern[, startrev]])`` + # Like ``follow([pattern[, startrev]])`` but follows only the first parent + # of every revisions or files revisions. return _follow(repo, subset, x, '_followfirst', followfirst=True) @predicate('all()', safe=True) @@ -1519,6 +1547,9 @@ # some optimisations from the fact this is a baseset. return subset & ps +def parentpost(repo, subset, x, order): + return p1(repo, subset, x) + @predicate('parents([set])', safe=True) def parents(repo, subset, x): """ @@ -1569,7 +1600,7 @@ target = phases.secret return _phase(repo, subset, target) -def parentspec(repo, subset, x, n): +def parentspec(repo, subset, x, n, order): """``set^0`` The set. ``set^1`` (or ``set^``), ``set^2`` @@ -1590,7 +1621,7 @@ ps.add(cl.parentrevs(r)[0]) elif n == 2: parents = cl.parentrevs(r) - if len(parents) > 1: + if parents[1] != node.nullrev: ps.add(parents[1]) return subset & ps @@ -1813,12 +1844,13 @@ return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs)) -@predicate('reverse(set)', safe=True) -def reverse(repo, subset, x): +@predicate('reverse(set)', safe=True, takeorder=True) +def reverse(repo, subset, x, order): """Reverse order of set. """ l = getset(repo, subset, x) - l.reverse() + if order == defineorder: + l.reverse() return l @predicate('roots(set)', safe=True) @@ -1880,8 +1912,8 @@ return args['set'], keyflags, opts -@predicate('sort(set[, [-]key... [, ...]])', safe=True) -def sort(repo, subset, x): +@predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True) +def sort(repo, subset, x, order): """Sort set by keys. The default sort order is ascending, specify a key as ``-key`` to sort in descending order. @@ -1902,7 +1934,7 @@ s, keyflags, opts = _getsortargs(x) revs = getset(repo, subset, s) - if not keyflags: + if not keyflags or order != defineorder: return revs if len(keyflags) == 1 and keyflags[0][0] == "rev": revs.sort(reverse=keyflags[0][1]) @@ -2233,9 +2265,7 @@ return baseset([node.wdirrev]) return baseset() -# for internal use -@predicate('_list', safe=True) -def _list(repo, subset, x): +def _orderedlist(repo, subset, x): s = getstring(x, "internal error") if not s: return baseset() @@ -2264,8 +2294,15 @@ return baseset(ls) # for internal use -@predicate('_intlist', safe=True) -def _intlist(repo, subset, x): +@predicate('_list', safe=True, takeorder=True) +def _list(repo, subset, x, order): + if order == followorder: + # slow path to take the subset order + return subset & _orderedlist(repo, fullreposet(repo), x) + else: + return _orderedlist(repo, subset, x) + +def _orderedintlist(repo, subset, x): s = getstring(x, "internal error") if not s: return baseset() @@ -2274,8 +2311,15 @@ return baseset([r for r in ls if r in s]) # for internal use -@predicate('_hexlist', safe=True) -def _hexlist(repo, subset, x): +@predicate('_intlist', safe=True, takeorder=True) +def _intlist(repo, subset, x, order): + if order == followorder: + # slow path to take the subset order + return subset & _orderedintlist(repo, fullreposet(repo), x) + else: + return _orderedintlist(repo, subset, x) + +def _orderedhexlist(repo, subset, x): s = getstring(x, "internal error") if not s: return baseset() @@ -2284,8 +2328,18 @@ s = subset return baseset([r for r in ls if r in s]) +# for internal use +@predicate('_hexlist', safe=True, takeorder=True) +def _hexlist(repo, subset, x, order): + if order == followorder: + # slow path to take the subset order + return subset & _orderedhexlist(repo, fullreposet(repo), x) + else: + return _orderedhexlist(repo, subset, x) + methods = { "range": rangeset, + "rangepre": rangepre, "dagrange": dagrange, "string": stringset, "symbol": stringset, @@ -2298,7 +2352,51 @@ "func": func, "ancestor": ancestorspec, "parent": parentspec, - "parentpost": p1, + "parentpost": parentpost, +} + +# Constants for ordering requirement, used in _analyze(): +# +# If 'define', any nested functions and operations can change the ordering of +# the entries in the set. If 'follow', any nested functions and operations +# should take the ordering specified by the first operand to the '&' operator. +# +# For instance, +# +# X & (Y | Z) +# ^ ^^^^^^^ +# | follow +# define +# +# will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order +# of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't. +# +# 'any' means the order doesn't matter. For instance, +# +# X & !Y +# ^ +# any +# +# 'y()' can either enforce its ordering requirement or take the ordering +# specified by 'x()' because 'not()' doesn't care the order. +# +# Transition of ordering requirement: +# +# 1. starts with 'define' +# 2. shifts to 'follow' by 'x & y' +# 3. changes back to 'define' on function call 'f(x)' or function-like +# operation 'x (f) y' because 'f' may have its own ordering requirement +# for 'x' and 'y' (e.g. 'first(x)') +# +anyorder = 'any' # don't care the order +defineorder = 'define' # should define the order +followorder = 'follow' # must follow the current order + +# transition table for 'x & y', from the current expression 'x' to 'y' +_tofolloworder = { + anyorder: anyorder, + defineorder: followorder, + followorder: followorder, } def _matchonly(revs, bases): @@ -2316,6 +2414,97 @@ and getsymbol(bases[1][1]) == 'ancestors'): return ('list', revs[2], bases[1][2]) +def _fixops(x): + """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be + handled well by our simple top-down parser""" + if not isinstance(x, tuple): + return x + + op = x[0] + if op == 'parent': + # x^:y means (x^) : y, not x ^ (:y) + # x^: means (x^) :, not x ^ (:) + post = ('parentpost', x[1]) + if x[2][0] == 'dagrangepre': + return _fixops(('dagrange', post, x[2][1])) + elif x[2][0] == 'rangepre': + return _fixops(('range', post, x[2][1])) + elif x[2][0] == 'rangeall': + return _fixops(('rangepost', post)) + elif op == 'or': + # make number of arguments deterministic: + # x + y + z -> (or x y z) -> (or (list x y z)) + return (op, _fixops(('list',) + x[1:])) + + return (op,) + tuple(_fixops(y) for y in x[1:]) + +def _analyze(x, order): + if x is None: + return x + + op = x[0] + if op == 'minus': + return _analyze(('and', x[1], ('not', x[2])), order) + elif op == 'only': + t = ('func', ('symbol', 'only'), ('list', x[1], x[2])) + return _analyze(t, order) + elif op == 'onlypost': + return _analyze(('func', ('symbol', 'only'), x[1]), order) + elif op == 'dagrangepre': + return _analyze(('func', ('symbol', 'ancestors'), x[1]), order) + elif op == 'dagrangepost': + return _analyze(('func', ('symbol', 'descendants'), x[1]), order) + elif op == 'rangeall': + return _analyze(('rangepre', ('string', 'tip')), order) + elif op == 'rangepost': + return _analyze(('range', x[1], ('string', 'tip')), order) + elif op == 'negate': + s = getstring(x[1], _("can't negate that")) + return _analyze(('string', '-' + s), order) + elif op in ('string', 'symbol'): + return x + elif op == 'and': + ta = _analyze(x[1], order) + tb = _analyze(x[2], _tofolloworder[order]) + return (op, ta, tb, order) + elif op == 'or': + return (op, _analyze(x[1], order), order) + elif op == 'not': + return (op, _analyze(x[1], anyorder), order) + elif op in ('rangepre', 'parentpost'): + return (op, _analyze(x[1], defineorder), order) + elif op == 'group': + return _analyze(x[1], order) + elif op in ('dagrange', 'range', 'parent', 'ancestor'): + ta = _analyze(x[1], defineorder) + tb = _analyze(x[2], defineorder) + return (op, ta, tb, order) + elif op == 'list': + return (op,) + tuple(_analyze(y, order) for y in x[1:]) + elif op == 'keyvalue': + return (op, x[1], _analyze(x[2], order)) + elif op == 'func': + f = getsymbol(x[1]) + d = defineorder + if f == 'present': + # 'present(set)' is known to return the argument set with no + # modification, so forward the current order to its argument + d = order + return (op, x[1], _analyze(x[2], d), order) + raise ValueError('invalid operator %r' % op) + +def analyze(x, order=defineorder): + """Transform raw parsed tree to evaluatable tree which can be fed to + optimize() or getset() + + All pseudo operations should be mapped to real operations or functions + defined in methods or symbols table respectively. + + 'order' specifies how the current expression 'x' is ordered (see the + constants defined above.) + """ + return _analyze(x, order) + def _optimize(x, small): if x is None: return 0, x @@ -2325,47 +2514,29 @@ smallbonus = .5 op = x[0] - if op == 'minus': - return _optimize(('and', x[1], ('not', x[2])), small) - elif op == 'only': - t = ('func', ('symbol', 'only'), ('list', x[1], x[2])) - return _optimize(t, small) - elif op == 'onlypost': - return _optimize(('func', ('symbol', 'only'), x[1]), small) - elif op == 'dagrangepre': - return _optimize(('func', ('symbol', 'ancestors'), x[1]), small) - elif op == 'dagrangepost': - return _optimize(('func', ('symbol', 'descendants'), x[1]), small) - elif op == 'rangeall': - return _optimize(('range', ('string', '0'), ('string', 'tip')), small) - elif op == 'rangepre': - return _optimize(('range', ('string', '0'), x[1]), small) - elif op == 'rangepost': - return _optimize(('range', x[1], ('string', 'tip')), small) - elif op == 'negate': - s = getstring(x[1], _("can't negate that")) - return _optimize(('string', '-' + s), small) - elif op in 'string symbol negate': + if op in ('string', 'symbol'): return smallbonus, x # single revisions are small elif op == 'and': wa, ta = _optimize(x[1], True) wb, tb = _optimize(x[2], True) + order = x[3] w = min(wa, wb) # (::x and not ::y)/(not ::y and ::x) have a fast path tm = _matchonly(ta, tb) or _matchonly(tb, ta) if tm: - return w, ('func', ('symbol', 'only'), tm) + return w, ('func', ('symbol', 'only'), tm, order) if tb is not None and tb[0] == 'not': - return wa, ('difference', ta, tb[1]) + return wa, ('difference', ta, tb[1], order) if wa > wb: - return w, (op, tb, ta) - return w, (op, ta, tb) + return w, (op, tb, ta, order) + return w, (op, ta, tb, order) elif op == 'or': # fast path for machine-generated expression, that is likely to have # lots of trivial revisions: 'a + b + c()' to '_list(a b) + c()' + order = x[2] ws, ts, ss = [], [], [] def flushss(): if not ss: @@ -2374,12 +2545,12 @@ w, t = ss[0] else: s = '\0'.join(t[1] for w, t in ss) - y = ('func', ('symbol', '_list'), ('string', s)) + y = ('func', ('symbol', '_list'), ('string', s), order) w, t = _optimize(y, False) ws.append(w) ts.append(t) del ss[:] - for y in x[1:]: + for y in getlist(x[1]): w, t = _optimize(y, False) if t is not None and (t[0] == 'string' or t[0] == 'symbol'): ss.append((w, t)) @@ -2393,33 +2564,27 @@ # we can't reorder trees by weight because it would change the order. # ("sort(a + b)" == "sort(b + a)", but "a + b" != "b + a") # ts = tuple(t for w, t in sorted(zip(ws, ts), key=lambda wt: wt[0])) - return max(ws), (op,) + tuple(ts) + return max(ws), (op, ('list',) + tuple(ts), order) elif op == 'not': # Optimize not public() to _notpublic() because we have a fast version - if x[1] == ('func', ('symbol', 'public'), None): - newsym = ('func', ('symbol', '_notpublic'), None) + if x[1][:3] == ('func', ('symbol', 'public'), None): + order = x[1][3] + newsym = ('func', ('symbol', '_notpublic'), None, order) o = _optimize(newsym, not small) return o[0], o[1] else: o = _optimize(x[1], not small) - return o[0], (op, o[1]) - elif op == 'parentpost': + order = x[2] + return o[0], (op, o[1], order) + elif op in ('rangepre', 'parentpost'): o = _optimize(x[1], small) - return o[0], (op, o[1]) - elif op == 'group': - 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) - elif x[2][0] == 'rangepre': - return _optimize(('range', post, x[2][1]), small) - + order = x[2] + return o[0], (op, o[1], order) + elif op in ('dagrange', 'range', 'parent', 'ancestor'): wa, ta = _optimize(x[1], small) wb, tb = _optimize(x[2], small) - return wa + wb, (op, ta, tb) + order = x[3] + return wa + wb, (op, ta, tb, order) elif op == 'list': ws, ts = zip(*(_optimize(y, small) for y in x[1:])) return sum(ws), (op,) + ts @@ -2429,32 +2594,36 @@ elif op == 'func': f = getsymbol(x[1]) wa, ta = _optimize(x[2], small) - if f in ("author branch closed date desc file grep keyword " - "outgoing user"): + if f in ('author', 'branch', 'closed', 'date', 'desc', 'file', 'grep', + 'keyword', 'outgoing', 'user', 'destination'): w = 10 # slow - elif f in "modifies adds removes": + elif f in ('modifies', 'adds', 'removes'): w = 30 # slower elif f == "contains": w = 100 # very slow elif f == "ancestor": w = 1 * smallbonus - elif f in "reverse limit first _intlist": + elif f in ('reverse', 'limit', 'first', '_intlist'): w = 0 - elif f in "sort": + elif f == "sort": w = 10 # assume most sorts look at changelog else: w = 1 - return w + wa, (op, x[1], ta) - return 1, x + order = x[3] + return w + wa, (op, x[1], ta, order) + raise ValueError('invalid operator %r' % op) def optimize(tree): + """Optimize evaluatable tree + + All pseudo operations should be transformed beforehand. + """ _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) +_aliassyminitletters = _syminitletters | set(pycompat.sysstr('$')) def _parsewith(spec, lookup=None, syminitletters=None): """Generate a parse tree of given spec with given tokenizing options @@ -2475,7 +2644,7 @@ syminitletters=syminitletters)) if pos != len(spec): raise error.ParseError(_('invalid token'), pos) - return parser.simplifyinfixops(tree, ('list', 'or')) + return _fixops(parser.simplifyinfixops(tree, ('list', 'or'))) class _aliasrules(parser.basealiasrules): """Parsing and expansion rule set of revset aliases""" @@ -2496,15 +2665,14 @@ if tree[0] == 'func' and tree[1][0] == 'symbol': return tree[1][1], getlist(tree[2]) -def expandaliases(ui, tree, showwarning=None): +def expandaliases(ui, tree): aliases = _aliasrules.buildmap(ui.configitems('revsetalias')) tree = _aliasrules.expand(aliases, tree) - if showwarning: - # warn about problematic (but not referred) aliases - for name, alias in sorted(aliases.iteritems()): - if alias.error and not alias.warned: - showwarning(_('warning: %s\n') % (alias.error)) - alias.warned = True + # warn about problematic (but not referred) aliases + for name, alias in sorted(aliases.iteritems()): + if alias.error and not alias.warned: + ui.warn(_('warning: %s\n') % (alias.error)) + alias.warned = True return tree def foldconcat(tree): @@ -2535,13 +2703,21 @@ # hook for extensions to execute code on the optimized tree pass -def match(ui, spec, repo=None): - """Create a matcher for a single revision spec.""" - return matchany(ui, [spec], repo=repo) - -def matchany(ui, specs, repo=None): +def match(ui, spec, repo=None, order=defineorder): + """Create a matcher for a single revision spec + + If order=followorder, a matcher takes the ordering specified by the input + set. + """ + return matchany(ui, [spec], repo=repo, order=order) + +def matchany(ui, specs, repo=None, order=defineorder): """Create a matcher that will include any revisions matching one of the - given specs""" + given specs + + If order=followorder, a matcher takes the ordering specified by the input + set. + """ if not specs: def mfunc(repo, subset=None): return baseset() @@ -2554,15 +2730,18 @@ if len(specs) == 1: tree = parse(specs[0], lookup) else: - tree = ('or',) + tuple(parse(s, lookup) for s in specs) - return _makematcher(ui, tree, repo) - -def _makematcher(ui, tree, repo): + tree = ('or', ('list',) + tuple(parse(s, lookup) for s in specs)) + if ui: - tree = expandaliases(ui, tree, showwarning=ui.warn) + tree = expandaliases(ui, tree) tree = foldconcat(tree) + tree = analyze(tree, order) tree = optimize(tree) posttreebuilthook(tree, repo) + return makematcher(tree) + +def makematcher(tree): + """Create a matcher from an evaluatable tree""" def mfunc(repo, subset=None): if subset is None: subset = fullreposet(repo)
--- a/mercurial/scmutil.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/scmutil.py Tue Oct 18 14:15:15 2016 -0500 @@ -256,17 +256,15 @@ raise return [] - def open(self, path, mode="r", text=False, atomictemp=False, - notindexed=False, backgroundclose=False): + @util.propertycache + def open(self): '''Open ``path`` file, which is relative to vfs root. Newly created directories are marked as "not to be indexed by the content indexing service", if ``notindexed`` is specified for "write" mode access. ''' - self.open = self.__call__ - return self.__call__(path, mode, text, atomictemp, notindexed, - backgroundclose=backgroundclose) + return self.__call__ def read(self, path): with self(path, 'rb') as fp: @@ -589,6 +587,12 @@ if nlink == 0: self._fixfilemode(f) + if checkambig: + if mode in ('r', 'rb'): + raise error.Abort(_('implementation error: mode %s is not' + ' valid for checkambig=True') % mode) + fp = checkambigatclosing(fp) + if backgroundclose: if not self._backgroundfilecloser: raise error.Abort(_('backgroundclose can only be used when a ' @@ -638,6 +642,14 @@ def mustaudit(self, onoff): self.vfs.mustaudit = onoff + @property + def options(self): + return self.vfs.options + + @options.setter + def options(self, value): + self.vfs.options = value + class filtervfs(abstractvfs, auditvfs): '''Wrapper vfs for filtering filenames with a function.''' @@ -741,7 +753,7 @@ if no HGRCPATH, use default os-specific path.''' global _rcpath if _rcpath is None: - if 'HGRCPATH' in os.environ: + if 'HGRCPATH' in encoding.environ: _rcpath = [] for p in os.environ['HGRCPATH'].split(os.pathsep): if not p: @@ -775,7 +787,6 @@ def _pairspec(revspec): tree = revset.parse(revspec) - tree = revset.optimize(tree) # fix up "x^:y" -> "(x^):y" return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall') def revpair(repo, revs): @@ -942,20 +953,12 @@ ret = 0 join = lambda f: os.path.join(prefix, f) - def matchessubrepo(matcher, subpath): - if matcher.exact(subpath): - return True - for f in matcher.files(): - if f.startswith(subpath): - return True - return False - wctx = repo[None] for subpath in sorted(wctx.substate): - if opts.get('subrepos') or matchessubrepo(m, subpath): + submatch = matchmod.subdirmatcher(subpath, m) + if opts.get('subrepos') or m.exact(subpath) or any(submatch.files()): sub = wctx.sub(subpath) try: - submatch = matchmod.subdirmatcher(subpath, m) if sub.addremove(submatch, prefix, opts, dry_run, similarity): ret = 1 except error.LookupError: @@ -1303,15 +1306,13 @@ # experimental config: format.generaldelta return ui.configbool('format', 'generaldelta', False) -class delayclosedfile(object): - """Proxy for a file object whose close is delayed. +class closewrapbase(object): + """Base class of wrapper, which hooks closing Do not instantiate outside of the vfs layer. """ - - def __init__(self, fh, closer): + def __init__(self, fh): object.__setattr__(self, '_origfh', fh) - object.__setattr__(self, '_closer', closer) def __getattr__(self, attr): return getattr(self._origfh, attr) @@ -1326,6 +1327,21 @@ return self._origfh.__enter__() def __exit__(self, exc_type, exc_value, exc_tb): + raise NotImplementedError('attempted instantiating ' + str(type(self))) + + def close(self): + raise NotImplementedError('attempted instantiating ' + str(type(self))) + +class delayclosedfile(closewrapbase): + """Proxy for a file object whose close is delayed. + + Do not instantiate outside of the vfs layer. + """ + def __init__(self, fh, closer): + super(delayclosedfile, self).__init__(fh) + object.__setattr__(self, '_closer', closer) + + def __exit__(self, exc_type, exc_value, exc_tb): self._closer.close(self._origfh) def close(self): @@ -1421,3 +1437,34 @@ return self._queue.put(fh, block=True, timeout=None) + +class checkambigatclosing(closewrapbase): + """Proxy for a file object, to avoid ambiguity of file stat + + See also util.filestat for detail about "ambiguity of file stat". + + This proxy is useful only if the target file is guarded by any + lock (e.g. repo.lock or repo.wlock) + + Do not instantiate outside of the vfs layer. + """ + def __init__(self, fh): + super(checkambigatclosing, self).__init__(fh) + object.__setattr__(self, '_oldstat', util.filestat(fh.name)) + + def _checkambig(self): + oldstat = self._oldstat + if oldstat.stat: + newstat = util.filestat(self._origfh.name) + if newstat.isambig(oldstat): + # stat of changed file is ambiguous to original one + advanced = (oldstat.stat.st_mtime + 1) & 0x7fffffff + os.utime(self._origfh.name, (advanced, advanced)) + + def __exit__(self, exc_type, exc_value, exc_tb): + self._origfh.__exit__(exc_type, exc_value, exc_tb) + self._checkambig() + + def close(self): + self._origfh.close() + self._checkambig()
--- a/mercurial/scmwindows.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/scmwindows.py Tue Oct 18 14:15:15 2016 -0500 @@ -1,6 +1,5 @@ from __future__ import absolute_import -import _winreg import os from . import ( @@ -8,6 +7,12 @@ util, ) +try: + import _winreg as winreg + winreg.CloseKey +except ImportError: + import winreg + def systemrcpath(): '''return default os-specific hgrc search path''' rcpath = [] @@ -23,7 +28,7 @@ rcpath.append(os.path.join(progrcd, f)) # else look for a system rcpath in the registry value = util.lookupreg('SOFTWARE\\Mercurial', None, - _winreg.HKEY_LOCAL_MACHINE) + winreg.HKEY_LOCAL_MACHINE) if not isinstance(value, str) or not value: return rcpath value = util.localpath(value)
--- a/mercurial/sshpeer.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/sshpeer.py Tue Oct 18 14:15:15 2016 -0500 @@ -232,13 +232,7 @@ __del__ = cleanup def _submitbatch(self, req): - cmds = [] - for op, argsdict in req: - args = ','.join('%s=%s' % (wireproto.escapearg(k), - wireproto.escapearg(v)) - for k, v in argsdict.iteritems()) - cmds.append('%s %s' % (op, args)) - rsp = self._callstream("batch", cmds=';'.join(cmds)) + rsp = self._callstream("batch", cmds=wireproto.encodebatchcmds(req)) available = self._getamount() # TODO this response parsing is probably suboptimal for large # batches with large responses. @@ -292,10 +286,7 @@ r = self._call(cmd, **args) if r: return '', r - while True: - d = fp.read(4096) - if not d: - break + for d in iter(lambda: fp.read(4096), ''): self._send(d) self._send("", flush=True) r = self._recv() @@ -308,10 +299,7 @@ if r: # XXX needs to be made better raise error.Abort(_('unexpected remote reply: %s') % r) - while True: - d = fp.read(4096) - if not d: - break + for d in iter(lambda: fp.read(4096), ''): self._send(d) self._send("", flush=True) return self.pipei @@ -353,10 +341,7 @@ d = self._call("addchangegroup") if d: self._abort(error.RepoError(_("push refused: %s") % d)) - while True: - d = cg.read(4096) - if not d: - break + for d in iter(lambda: cg.read(4096), ''): self.pipeo.write(d) self.readerr()
--- a/mercurial/sshserver.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/sshserver.py Tue Oct 18 14:15:15 2016 -0500 @@ -68,12 +68,12 @@ def redirect(self): pass - def groupchunks(self, changegroup): - while True: - d = changegroup.read(4096) - if not d: - break - yield d + def groupchunks(self, fh): + return iter(lambda: fh.read(4096), '') + + def compresschunks(self, chunks): + for chunk in chunks: + yield chunk def sendresponse(self, v): self.fout.write("%d\n" % len(v))
--- a/mercurial/sslutil.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/sslutil.py Tue Oct 18 14:15:15 2016 -0500 @@ -390,8 +390,12 @@ try: sslcontext.load_verify_locations(cafile=settings['cafile']) except ssl.SSLError as e: + if len(e.args) == 1: # pypy has different SSLError args + msg = e.args[0] + else: + msg = e.args[1] raise error.Abort(_('error loading CA file %s: %s') % ( - settings['cafile'], e.args[1]), + settings['cafile'], msg), hint=_('file is empty or malformed?')) caloaded = True elif settings['allowloaddefaultcerts']:
--- a/mercurial/statichttprepo.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/statichttprepo.py Tue Oct 18 14:15:15 2016 -0500 @@ -181,6 +181,9 @@ def lock(self, wait=True): raise error.Abort(_('cannot lock static-http repository')) + def _writecaches(self): + pass # statichttprepository are read only + def instance(ui, path, create): if create: raise error.Abort(_('cannot create new static-http repository'))
--- a/mercurial/store.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/store.py Tue Oct 18 14:15:15 2016 -0500 @@ -16,6 +16,7 @@ from . import ( error, parsers, + pycompat, scmutil, util, ) @@ -65,7 +66,7 @@ these characters will be escaped by encodefunctions ''' - winreserved = [ord(x) for x in '\\:*?"<>|'] + winreserved = [ord(x) for x in u'\\:*?"<>|'] for x in range(32): yield x for x in range(126, 256): @@ -98,11 +99,20 @@ 'the\\x07quick\\xadshot' ''' e = '_' - cmap = dict([(chr(x), chr(x)) for x in xrange(127)]) + if pycompat.ispy3: + xchr = lambda x: bytes([x]) + asciistr = bytes(xrange(127)) + else: + xchr = chr + asciistr = map(chr, xrange(127)) + capitals = list(range(ord("A"), ord("Z") + 1)) + + cmap = dict((x, x) for x in asciistr) for x in _reserved(): - cmap[chr(x)] = "~%02x" % x - for x in list(range(ord("A"), ord("Z") + 1)) + [ord(e)]: - cmap[chr(x)] = e + chr(x).lower() + cmap[xchr(x)] = "~%02x" % x + for x in capitals + [ord(e)]: + cmap[xchr(x)] = e + xchr(x).lower() + dmap = {} for k, v in cmap.iteritems(): dmap[v] = k
--- a/mercurial/streamclone.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/streamclone.py Tue Oct 18 14:15:15 2016 -0500 @@ -299,6 +299,20 @@ repo.ui.progress(_('clone'), 0, total=bytecount, unit=_('bytes')) start = time.time() + # TODO: get rid of (potential) inconsistency + # + # If transaction is started and any @filecache property is + # changed at this point, it causes inconsistency between + # in-memory cached property and streamclone-ed file on the + # disk. Nested transaction prevents transaction scope "clone" + # below from writing in-memory changes out at the end of it, + # even though in-memory changes are discarded at the end of it + # regardless of transaction nesting. + # + # But transaction nesting can't be simply prohibited, because + # nesting occurs also in ordinary case (e.g. enabling + # clonebundles). + with repo.transaction('clone'): with repo.svfs.backgroundclosing(repo.ui, expectedcount=filecount): for i in xrange(filecount): @@ -322,8 +336,9 @@ total=bytecount, unit=_('bytes')) ofp.write(chunk) - # Writing straight to files circumvented the inmemory caches - repo.invalidate() + # force @filecache properties to be reloaded from + # streamclone-ed file at next access + repo.invalidate(clearfilecache=True) elapsed = time.time() - start if elapsed <= 0:
--- a/mercurial/subrepo.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/subrepo.py Tue Oct 18 14:15:15 2016 -0500 @@ -26,6 +26,7 @@ config, error, exchange, + filemerge, match as matchmod, node, pathutil, @@ -174,7 +175,7 @@ if state[s][1] != nullstate[1]] repo.wwrite('.hgsubstate', ''.join(lines), '') -def submerge(repo, wctx, mctx, actx, overwrite): +def submerge(repo, wctx, mctx, actx, overwrite, labels=None): """delegated from merge.applyupdates: merging of .hgsubstate file in working context, merging context and ancestor context""" if mctx == actx: # backwards? @@ -200,6 +201,8 @@ a = ld if s in s2: + prompts = filemerge.partextras(labels) + prompts['s'] = s r = s2[s] if ld == r or r == a: # no change or local is newer sm[s] = l @@ -209,10 +212,13 @@ wctx.sub(s).get(r, overwrite) sm[s] = r elif ld[0] != r[0]: # sources differ + prompts['lo'] = l[0] + prompts['ro'] = r[0] if repo.ui.promptchoice( - _(' subrepository sources for %s differ\n' - 'use (l)ocal source (%s) or (r)emote source (%s)?' - '$$ &Local $$ &Remote') % (s, l[0], r[0]), 0): + _(' subrepository sources for %(s)s differ\n' + 'use (l)ocal%(l)s source (%(lo)s)' + ' or (r)emote%(o)s source (%(ro)s)?' + '$$ &Local $$ &Remote') % prompts, 0): debug(s, "prompt changed, get", r) wctx.sub(s).get(r, overwrite) sm[s] = r @@ -223,12 +229,14 @@ else: debug(s, "both sides changed") srepo = wctx.sub(s) + prompts['sl'] = srepo.shortid(l[1]) + prompts['sr'] = srepo.shortid(r[1]) option = repo.ui.promptchoice( - _(' subrepository %s diverged (local revision: %s, ' - 'remote revision: %s)\n' - '(M)erge, keep (l)ocal or keep (r)emote?' + _(' subrepository %(s)s diverged (local revision: %(sl)s, ' + 'remote revision: %(sr)s)\n' + '(M)erge, keep (l)ocal%(l)s or keep (r)emote%(o)s?' '$$ &Merge $$ &Local $$ &Remote') - % (s, srepo.shortid(l[1]), srepo.shortid(r[1])), 0) + % prompts, 0) if option == 0: wctx.sub(s).merge(r) sm[s] = l @@ -249,9 +257,10 @@ continue else: if repo.ui.promptchoice( - _(' local changed subrepository %s which remote removed\n' + _(' local%(l)s changed subrepository %(s)s' + ' which remote%(o)s removed\n' 'use (c)hanged version or (d)elete?' - '$$ &Changed $$ &Delete') % s, 0): + '$$ &Changed $$ &Delete') % prompts, 0): debug(s, "prompt remove") wctx.sub(s).remove() @@ -264,9 +273,10 @@ sm[s] = r elif r != sa[s]: if repo.ui.promptchoice( - _(' remote changed subrepository %s which local removed\n' + _(' remote%(o)s changed subrepository %(s)s' + ' which local%(l)s removed\n' 'use (c)hanged version or (d)elete?' - '$$ &Changed $$ &Delete') % s, 0) == 0: + '$$ &Changed $$ &Delete') % prompts, 0) == 0: debug(s, "prompt recreate", r) mctx.sub(s).get(r) sm[s] = r
--- a/mercurial/templatekw.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templatekw.py Tue Oct 18 14:15:15 2016 -0500 @@ -26,14 +26,11 @@ # "{get(extras, key)}" class _hybrid(object): - def __init__(self, gen, values, makemap, joinfmt=None): + def __init__(self, gen, values, makemap, joinfmt): self.gen = gen self.values = values self._makemap = makemap - if joinfmt: - self.joinfmt = joinfmt - else: - self.joinfmt = lambda x: x.values()[0] + self.joinfmt = joinfmt def __iter__(self): return self.gen def itermaps(self): @@ -53,7 +50,7 @@ if not element: element = name f = _showlist(name, values, plural, separator, **args) - return _hybrid(f, values, lambda x: {element: x}) + return _hybrid(f, values, lambda x: {element: x}, lambda d: d[element]) def _showlist(name, values, plural=None, separator=' ', **args): '''expand set of values. @@ -592,5 +589,10 @@ for name, func in registrarobj._table.iteritems(): keywords[name] = func +@templatekeyword('termwidth') +def termwidth(repo, ctx, templ, **args): + """Integer. The width of the current terminal.""" + return repo.ui.termwidth() + # tell hggettext to extract docstrings from these functions: i18nfunctions = keywords.values()
--- a/mercurial/templater.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templater.py Tue Oct 18 14:15:15 2016 -0500 @@ -33,6 +33,10 @@ "|": (5, None, None, ("|", 5), None), "%": (6, None, None, ("%", 6), None), ")": (0, None, None, None, None), + "+": (3, None, None, ("+", 3), None), + "-": (3, None, ("negate", 10), ("-", 3), None), + "*": (4, None, None, ("*", 4), None), + "/": (4, None, None, ("/", 4), None), "integer": (0, "integer", None, None, None), "symbol": (0, "symbol", None, None, None), "string": (0, "string", None, None, None), @@ -48,7 +52,7 @@ c = program[pos] if c.isspace(): # skip inter-token whitespace pass - elif c in "(,)%|": # handle simple operators + elif c in "(,)%|+-*/": # handle simple operators yield (c, None, pos) elif c in '"\'': # handle quoted templates s = pos + 1 @@ -70,13 +74,8 @@ pos += 1 else: raise error.ParseError(_("unterminated string"), s) - elif c.isdigit() or c == '-': + elif c.isdigit(): s = pos - if c == '-': # simply take negate operator as part of integer - pos += 1 - if pos >= end or not program[pos].isdigit(): - raise error.ParseError(_("integer literal without digits"), s) - pos += 1 while pos < end: d = program[pos] if not d.isdigit(): @@ -289,6 +288,22 @@ thing = stringify(thing) return thing +def evalboolean(context, mapping, arg): + """Evaluate given argument as boolean, but also takes boolean literals""" + func, data = arg + if func is runsymbol: + thing = func(context, mapping, data, default=None) + if thing is None: + # not a template keyword, takes as a boolean literal + thing = util.parsebool(data) + else: + thing = func(context, mapping, data) + if isinstance(thing, bool): + return thing + # other objects are evaluated as strings, which means 0 is True, but + # empty dict/list should be False as they are expected to be '' + return bool(stringify(thing)) + def evalinteger(context, mapping, arg, err): v = evalfuncarg(context, mapping, arg) try: @@ -404,6 +419,31 @@ # If so, return the expanded value. yield i +def buildnegate(exp, context): + arg = compileexp(exp[1], context, exprmethods) + return (runnegate, arg) + +def runnegate(context, mapping, data): + data = evalinteger(context, mapping, data, + _('negation needs an integer argument')) + return -data + +def buildarithmetic(exp, context, func): + left = compileexp(exp[1], context, exprmethods) + right = compileexp(exp[2], context, exprmethods) + return (runarithmetic, (func, left, right)) + +def runarithmetic(context, mapping, data): + func, left, right = data + left = evalinteger(context, mapping, left, + _('arithmetic only defined on integers')) + right = evalinteger(context, mapping, right, + _('arithmetic only defined on integers')) + try: + return func(left, right) + except ZeroDivisionError: + raise error.Abort(_('division by zero is not defined')) + def buildfunc(exp, context): n = getsymbol(exp[1]) args = [compileexp(x, context, exprmethods) for x in getlist(exp[2])] @@ -464,6 +504,20 @@ return ''.join(chunks) +@templatefunc('files(pattern)') +def files(context, mapping, args): + """All files of the current changeset matching the pattern. See + :hg:`help patterns`.""" + if not len(args) == 1: + # i18n: "files" is a keyword + raise error.ParseError(_("files expects one argument")) + + raw = evalstring(context, mapping, args[0]) + ctx = mapping['ctx'] + m = ctx.match([raw]) + files = list(ctx.matches(m)) + return templatekw.showlist("file", files, **mapping) + @templatefunc('fill(text[, width[, initialident[, hangindent]]])') def fill(context, mapping, args): """Fill many @@ -488,7 +542,7 @@ return templatefilters.fill(text, width, initindent, hangindent) -@templatefunc('pad(text, width[, fillchar=\' \'[, right=False]])') +@templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])') def pad(context, mapping, args): """Pad text with a fill character.""" @@ -502,14 +556,14 @@ text = evalstring(context, mapping, args[0]) - right = False + left = False fillchar = ' ' if len(args) > 2: fillchar = evalstring(context, mapping, args[2]) if len(args) > 3: - right = util.parsebool(args[3][1]) + left = evalboolean(context, mapping, args[3]) - if right: + if left: return text.rjust(width, fillchar) else: return text.ljust(width, fillchar) @@ -560,24 +614,24 @@ # i18n: "if" is a keyword raise error.ParseError(_("if expects two or three arguments")) - test = evalstring(context, mapping, args[0]) + test = evalboolean(context, mapping, args[0]) if test: yield args[1][0](context, mapping, args[1][1]) elif len(args) == 3: yield args[2][0](context, mapping, args[2][1]) -@templatefunc('ifcontains(search, thing, then[, else])') +@templatefunc('ifcontains(needle, haystack, then[, else])') def ifcontains(context, mapping, args): """Conditionally execute based - on whether the item "search" is in "thing".""" + on whether the item "needle" is in "haystack".""" if not (3 <= len(args) <= 4): # i18n: "ifcontains" is a keyword raise error.ParseError(_("ifcontains expects three or four arguments")) - item = evalstring(context, mapping, args[0]) - items = evalfuncarg(context, mapping, args[1]) + needle = evalstring(context, mapping, args[0]) + haystack = evalfuncarg(context, mapping, args[1]) - if item in items: + if needle in haystack: yield args[2][0](context, mapping, args[2][1]) elif len(args) == 4: yield args[3][0](context, mapping, args[3][1]) @@ -683,6 +737,28 @@ tzoffset = util.makedate()[1] return (date[0], tzoffset) +@templatefunc('mod(a, b)') +def mod(context, mapping, args): + """Calculate a mod b such that a / b + a mod b == a""" + if not len(args) == 2: + # i18n: "mod" is a keyword + raise error.ParseError(_("mod expects two arguments")) + + func = lambda a, b: a % b + return runarithmetic(context, mapping, (func, args[0], args[1])) + +@templatefunc('relpath(path)') +def relpath(context, mapping, args): + """Convert a repository-absolute path into a filesystem path relative to + the current working directory.""" + if len(args) != 1: + # i18n: "relpath" is a keyword + raise error.ParseError(_("relpath expects one argument")) + + repo = mapping['ctx'].repo() + path = evalstring(context, mapping, args[0]) + return repo.pathto(path) + @templatefunc('revset(query[, formatargs...])') def revset(context, mapping, args): """Execute a revision set query. See @@ -884,6 +960,11 @@ "|": buildfilter, "%": buildmap, "func": buildfunc, + "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b), + "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b), + "negate": buildnegate, + "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b), + "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b), } # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"}) @@ -917,17 +998,19 @@ '''yield a single stream from a possibly nested set of iterators''' if isinstance(thing, str): yield thing + elif thing is None: + pass elif not util.safehasattr(thing, '__iter__'): - if thing is not None: - yield str(thing) + yield str(thing) else: for i in thing: if isinstance(i, str): yield i + elif i is None: + pass elif not util.safehasattr(i, '__iter__'): - if i is not None: - yield str(i) - elif i is not None: + yield str(i) + else: for j in _flatten(i): yield j @@ -1026,6 +1109,29 @@ raise error.ParseError(_('unmatched quotes'), conf.source('', key)) cache[key] = unquotestring(val) + elif key == "__base__": + # treat as a pointer to a base class for this style + path = util.normpath(os.path.join(base, val)) + + # fallback check in template paths + if not os.path.exists(path): + for p in templatepaths(): + p2 = util.normpath(os.path.join(p, val)) + if os.path.isfile(p2): + path = p2 + break + p3 = util.normpath(os.path.join(p2, "map")) + if os.path.isfile(p3): + path = p3 + break + + bcache, btmap = _readmapfile(path) + for k in bcache: + if k not in cache: + cache[k] = bcache[k] + for k in btmap: + if k not in tmap: + tmap[k] = btmap[k] else: val = 'default', val if ':' in val[1]:
--- a/mercurial/templates/coal/map Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/coal/map Tue Oct 18 14:15:15 2016 -0500 @@ -1,34 +1,2 @@ -%include paper/map - -footer = ../paper/footer.tmpl -search = ../paper/search.tmpl - -changelog = ../paper/shortlog.tmpl -shortlog = ../paper/shortlog.tmpl -shortlogentry = ../paper/shortlogentry.tmpl -graph = ../paper/graph.tmpl - -help = ../paper/help.tmpl -helptopics = ../paper/helptopics.tmpl - -diffstatlink = ../paper/diffstat.tmpl -diffstatnolink = ../paper/diffstat.tmpl -changelogentry = ../paper/shortlogentry.tmpl -searchentry = ../paper/shortlogentry.tmpl -changeset = ../paper/changeset.tmpl -manifest = ../paper/manifest.tmpl - -filerevision = ../paper/filerevision.tmpl -fileannotate = ../paper/fileannotate.tmpl -filediff = ../paper/filediff.tmpl -filecomparison = ../paper/filecomparison.tmpl -filelog = ../paper/filelog.tmpl -filelogentry = ../paper/filelogentry.tmpl - -tags = ../paper/tags.tmpl -bookmarks = ../paper/bookmarks.tmpl -branches = ../paper/branches.tmpl - -index = ../paper/index.tmpl -notfound = ../paper/notfound.tmpl -error = ../paper/error.tmpl +__base__ = ../paper/map +header = header.tmpl
--- a/mercurial/templates/gitweb/helptopics.tmpl Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/gitweb/helptopics.tmpl Tue Oct 18 14:15:15 2016 -0500 @@ -30,7 +30,7 @@ <div class="title"> </div> <table cellspacing="0"> -<tr><td colspan="2"><h2><a name="main" href="#topics">Topics</a></h2></td></tr> +<tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr> {topics % helpentry} {if(earlycommands, '
--- a/mercurial/templates/gitweb/map Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/gitweb/map Tue Oct 18 14:15:15 2016 -0500 @@ -137,13 +137,6 @@ <td class="{type}"><pre><a class="linenr" href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</pre></td> </tr>' -changelogparent = ' - <tr> - <th class="parent">parent {rev}:</th> - <td class="parent"> - <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> - </td> - </tr>' changesetlink = '<a class="list" href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>' changesetbranch = '<tr><td>branch</td><td>{name|escape}</td></tr>' changesetparent = ' @@ -182,11 +175,6 @@ </a> </td> </tr>' -changelogchild = ' - <tr> - <th class="child">child {rev}:</th> - <td class="child"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' changesetchild = ' <tr> <td>child {rev}</td> @@ -258,11 +246,6 @@ </a> </td> </tr>' -filelogparent = ' - <tr> - <td align="right">parent {rev}: </td> - <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' filediffchild = ' <tr> <td>child {rev}</td> @@ -277,11 +260,6 @@ <a class="list" href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a> </td> </tr>' -filelogchild = ' - <tr> - <td align="right">child {rev}: </td> - <td><a href="{url|urlescape}file{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' shortlog = shortlog.tmpl graph = graph.tmpl tagtag = '<span class="tagtag" title="{name|escape}">{name|escape}</span> '
--- a/mercurial/templates/gitweb/summary.tmpl Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/gitweb/summary.tmpl Tue Oct 18 14:15:15 2016 -0500 @@ -60,7 +60,7 @@ <table cellspacing="0"> {branches%branchentry} <tr class="light"> - <td colspan="4"><a class="list" href="{url|urlescape}branches{sessionvars%urlparameter}">...</a></td> + <td colspan="3"><a class="list" href="{url|urlescape}branches{sessionvars%urlparameter}">...</a></td> </tr> </table> {footer}
--- a/mercurial/templates/monoblue/helptopics.tmpl Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/monoblue/helptopics.tmpl Tue Oct 18 14:15:15 2016 -0500 @@ -35,7 +35,7 @@ <h2 class="no-link no-border">help</h2> <table cellspacing="0"> - <tr><td colspan="2"><h2><a name="main" href="#topics">Topics</a></h2></td></tr> + <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr> {topics % helpentry} {if(earlycommands, '
--- a/mercurial/templates/monoblue/map Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/monoblue/map Tue Oct 18 14:15:15 2016 -0500 @@ -136,13 +136,6 @@ </tr>' changesetlink = '<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>' -changelogparent = ' - <tr> - <th class="parent">parent {rev}:</th> - <td class="parent"> - {changesetlink} - </td> - </tr>' changesetbranch = '<dt>branch</dt><dd>{name|escape}</dd>' changesetparent = ' <dt>parent {rev}</dt> @@ -168,9 +161,6 @@ {rename%filerename}{node|short} </a> </dd>' -changelogchild = ' - <dt>child {rev}:</dt> - <dd><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>' changesetchild = ' <dt>child {rev}</dt> <dd><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></dd>' @@ -224,22 +214,12 @@ filecompparent = ' <dt>parent {rev}</dt> <dd><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>' -filelogparent = ' - <tr> - <td align="right">parent {rev}: </td> - <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' filediffchild = ' <dt>child {rev}</dt> <dd><a href="{url|urlescape}diff/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>' filecompchild = ' <dt>child {rev}</dt> <dd><a href="{url|urlescape}comparison/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></dd>' -filelogchild = ' - <tr> - <td align="right">child {rev}: </td> - <td><a href="{url|urlescape}file{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' shortlog = shortlog.tmpl tagtag = '<span class="tagtag" title="{name|escape}">{name|escape}</span> ' branchtag = '<span class="branchtag" title="{name|escape}">{name|escape}</span> '
--- a/mercurial/templates/monoblue/summary.tmpl Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/monoblue/summary.tmpl Tue Oct 18 14:15:15 2016 -0500 @@ -71,7 +71,7 @@ <table> {branches%branchentry} <tr class="light"> - <td colspan="4"><a class="list" href="{url|urlescape}branches{sessionvars%urlparameter}">...</a></td> + <td colspan="3"><a class="list" href="{url|urlescape}branches{sessionvars%urlparameter}">...</a></td> </tr> </table> {footer}
--- a/mercurial/templates/paper/helptopics.tmpl Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/paper/helptopics.tmpl Tue Oct 18 14:15:15 2016 -0500 @@ -32,7 +32,7 @@ <div id="hint">{searchhint}</div> </form> <table class="bigtable"> -<tr><td colspan="2"><h2><a name="main" href="#topics">Topics</a></h2></td></tr> +<tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr> {topics % helpentry} {if(earlycommands, '
--- a/mercurial/templates/paper/map Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/paper/map Tue Oct 18 14:15:15 2016 -0500 @@ -118,12 +118,6 @@ <td class="source {type}"><a href="#{lineid}">{rightlinenumber}</a> {rightline|escape}</td> </tr>' -changelogparent = ' - <tr> - <th class="parent">parent {rev}:</th> - <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' - changesetparent = '<a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> ' changesetparentdiff = ' @@ -153,15 +147,6 @@ </td> </tr>' changesetchild = ' <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a>' -changelogchild = ' - <tr> - <th class="child">child</th> - <td class="child"> - <a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}"> - {node|short} - </a> - </td> - </tr>' fileannotatechild = ' <tr> <td class="metatag">child:</td> @@ -224,22 +209,12 @@ <th class="parent">parent {rev}:</th> <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td> </tr>' -filelogparent = ' - <tr> - <th>parent {rev}:</th> - <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' filediffchild = ' <tr> <th class="child">child {rev}:</th> <td class="child"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a> </td> </tr>' -filelogchild = ' - <tr> - <th>child {rev}:</th> - <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' indexentry = ' <tr>
--- a/mercurial/templates/spartan/map Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/spartan/map Tue Oct 18 14:15:15 2016 -0500 @@ -170,21 +170,11 @@ <th class="parent">parent {rev}:</th> <td class="parent"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td> </tr>' -filelogparent = ' - <tr> - <th>parent {rev}:</th> - <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' filediffchild = ' <tr> <th class="child">child {rev}:</th> <td class="child"><a href="{url|urlescape}rev/{node|short}{sessionvars%urlparameter}">{node|short}</a></td> </tr>' -filelogchild = ' - <tr> - <th>child {rev}:</th> - <td><a href="{url|urlescape}file/{node|short}/{file|urlescape}{sessionvars%urlparameter}">{node|short}</a></td> - </tr>' indexentry = ' <tr class="parity{parity}"> <td><a href="{url|urlescape}{sessionvars%urlparameter}">{name|escape}</a></td>
--- a/mercurial/templates/static/style-gitweb.css Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/static/style-gitweb.css Tue Oct 18 14:15:15 2016 -0500 @@ -55,6 +55,9 @@ div.search { margin:4px 8px; position:absolute; top:56px; right:12px } tr.thisrev a { color:#999999; text-decoration: none; } tr.thisrev pre { color:#009900; } +td.annotate { + white-space: nowrap; +} div.annotate-info { display: none; position: absolute;
--- a/mercurial/templates/static/style-monoblue.css Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/static/style-monoblue.css Tue Oct 18 14:15:15 2016 -0500 @@ -335,6 +335,9 @@ } tr.thisrev a { color:#999999; text-decoration: none; } tr.thisrev td.source { color:#009900; } +td.annotate { + white-space: nowrap; +} div.annotate-info { display: none; position: absolute;
--- a/mercurial/templates/static/style-paper.css Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/static/style-paper.css Tue Oct 18 14:15:15 2016 -0500 @@ -210,6 +210,9 @@ .bigtable td.source { font-size: inherit; } tr.thisrev a { color:#999999; text-decoration: none; } tr.thisrev td.source { color:#009900; } +td.annotate { + white-space: nowrap; +} div.annotate-info { display: none; position: absolute;
--- a/mercurial/templates/static/style.css Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/templates/static/style.css Tue Oct 18 14:15:15 2016 -0500 @@ -12,6 +12,9 @@ .annotate { font-size: smaller; text-align: right; padding-right: 1em; } tr.thisrev a { color:#999999; text-decoration: none; } tr.thisrev pre { color:#009900; } +td.annotate { + white-space: nowrap; +} div.annotate-info { display: none; position: absolute;
--- a/mercurial/transaction.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/transaction.py Tue Oct 18 14:15:15 2016 -0500 @@ -48,7 +48,7 @@ for f, o, _ignore in entries: if o or not unlink: try: - fp = opener(f, 'a') + fp = opener(f, 'a', checkambig=True) fp.truncate(o) fp.close() except IOError:
--- a/mercurial/ui.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/ui.py Tue Oct 18 14:15:15 2016 -0500 @@ -33,7 +33,7 @@ samplehgrcs = { 'user': -"""# example user config (see "hg help config" for more info) +"""# example user config (see 'hg help config' for more info) [ui] # name and email, e.g. # username = Jane Doe <jdoe@example.com> @@ -41,18 +41,18 @@ [extensions] # uncomment these lines to enable some popular extensions -# (see "hg help extensions" for more info) +# (see 'hg help extensions' for more info) # # pager = # color =""", 'cloned': -"""# example repository config (see "hg help config" for more info) +"""# example repository config (see 'hg help config' for more info) [paths] default = %s # path aliases to other clones of this repo in URLs or filesystem paths -# (see "hg help config.paths" for more info) +# (see 'hg help config.paths' for more info) # # default-push = ssh://jdoe@example.net/hg/jdoes-fork # my-fork = ssh://jdoe@example.net/hg/jdoes-fork @@ -64,10 +64,10 @@ """, 'local': -"""# example repository config (see "hg help config" for more info) +"""# example repository config (see 'hg help config' for more info) [paths] # path aliases to other clones of this repo in URLs or filesystem paths -# (see "hg help config.paths" for more info) +# (see 'hg help config.paths' for more info) # # default = http://example.com/hg/example-repo # default-push = ssh://jdoe@example.net/hg/jdoes-fork @@ -80,11 +80,11 @@ """, 'global': -"""# example system-wide hg config (see "hg help config" for more info) +"""# example system-wide hg config (see 'hg help config' for more info) [extensions] # uncomment these lines to enable some popular extensions -# (see "hg help extensions" for more info) +# (see 'hg help extensions' for more info) # # blackbox = # color = @@ -1175,6 +1175,7 @@ % ((msg,) + calframe[stacklevel][1:4])) self.log('develwarn', '%s at: %s:%s (%s)\n', msg, *calframe[stacklevel][1:4]) + curframe = calframe = None # avoid cycles def deprecwarn(self, msg, version): """issue a deprecation warning
--- a/mercurial/url.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/url.py Tue Oct 18 14:15:15 2016 -0500 @@ -151,35 +151,6 @@ return _sendfile has_https = util.safehasattr(urlreq, 'httpshandler') -if has_https: - try: - _create_connection = socket.create_connection - except AttributeError: - _GLOBAL_DEFAULT_TIMEOUT = object() - - def _create_connection(address, timeout=_GLOBAL_DEFAULT_TIMEOUT, - source_address=None): - # lifted from Python 2.6 - - msg = "getaddrinfo returns an empty list" - host, port = address - for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM): - af, socktype, proto, canonname, sa = res - sock = None - try: - sock = socket.socket(af, socktype, proto) - if timeout is not _GLOBAL_DEFAULT_TIMEOUT: - sock.settimeout(timeout) - if source_address: - sock.bind(source_address) - sock.connect(sa) - return sock - - except socket.error as msg: - if sock is not None: - sock.close() - - raise socket.error(msg) class httpconnection(keepalive.HTTPConnection): # must be able to send big bundle as stream. @@ -237,18 +208,14 @@ version, status, reason = res._read_status() if status != httplib.CONTINUE: break - while True: - skip = res.fp.readline().strip() - if not skip: - break + # skip lines that are all whitespace + list(iter(lambda: res.fp.readline().strip(), '')) res.status = status res.reason = reason.strip() if res.status == 200: - while True: - line = res.fp.readline() - if line == '\r\n': - break + # skip lines until we find a blank line + list(iter(res.fp.readline, '\r\n')) return True if version == 'HTTP/1.0': @@ -337,7 +304,7 @@ self.cert_file = cert_file def connect(self): - self.sock = _create_connection((self.host, self.port)) + self.sock = socket.create_connection((self.host, self.port)) host = self.host if self.realhostport: # use CONNECT proxy
--- a/mercurial/util.h Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/util.h Tue Oct 18 14:15:15 2016 -0500 @@ -11,53 +11,8 @@ #include "compat.h" #if PY_MAJOR_VERSION >= 3 - #define IS_PY3K -#define PyInt_FromLong PyLong_FromLong -#define PyInt_AsLong PyLong_AsLong - -/* - Mapping of some of the python < 2.x PyString* functions to py3k's PyUnicode. - - The commented names below represent those that are present in the PyBytes - definitions for python < 2.6 (below in this file) that don't have a direct - implementation. -*/ - -#define PyStringObject PyUnicodeObject -#define PyString_Type PyUnicode_Type - -#define PyString_Check PyUnicode_Check -#define PyString_CheckExact PyUnicode_CheckExact -#define PyString_CHECK_INTERNED PyUnicode_CHECK_INTERNED -#define PyString_AS_STRING PyUnicode_AsLatin1String -#define PyString_GET_SIZE PyUnicode_GET_SIZE - -#define PyString_FromStringAndSize PyUnicode_FromStringAndSize -#define PyString_FromString PyUnicode_FromString -#define PyString_FromFormatV PyUnicode_FromFormatV -#define PyString_FromFormat PyUnicode_FromFormat -/* #define PyString_Size PyUnicode_GET_SIZE */ -/* #define PyString_AsString */ -/* #define PyString_Repr */ -#define PyString_Concat PyUnicode_Concat -#define PyString_ConcatAndDel PyUnicode_AppendAndDel -#define _PyString_Resize PyUnicode_Resize -/* #define _PyString_Eq */ -#define PyString_Format PyUnicode_Format -/* #define _PyString_FormatLong */ -/* #define PyString_DecodeEscape */ -#define _PyString_Join PyUnicode_Join -#define PyString_Decode PyUnicode_Decode -#define PyString_Encode PyUnicode_Encode -#define PyString_AsEncodedObject PyUnicode_AsEncodedObject -#define PyString_AsEncodedString PyUnicode_AsEncodedString -#define PyString_AsDecodedObject PyUnicode_AsDecodedObject -#define PyString_AsDecodedString PyUnicode_AsDecodedUnicode -/* #define PyString_AsStringAndSize */ -#define _PyString_InsertThousandsGrouping _PyUnicode_InsertThousandsGrouping - -#endif /* PY_MAJOR_VERSION */ +#endif typedef struct { PyObject_HEAD
--- a/mercurial/util.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/util.py Tue Oct 18 14:15:15 2016 -0500 @@ -28,6 +28,7 @@ import shutil import signal import socket +import string import subprocess import sys import tempfile @@ -59,7 +60,8 @@ 'socketserver', 'xmlrpclib', ): - globals()[attr] = getattr(pycompat, attr) + a = pycompat.sysstr(attr) + globals()[a] = getattr(pycompat, a) # This line is to make pyflakes happy: urlreq = pycompat.urlreq @@ -232,7 +234,7 @@ try: buffer = buffer except NameError: - if sys.version_info[0] < 3: + if not pycompat.ispy3: def buffer(sliceable, offset=0): return sliceable[offset:] else: @@ -561,7 +563,7 @@ Holds a reference to nodes on either side as well as a key-value pair for the dictionary entry. """ - __slots__ = ('next', 'prev', 'key', 'value') + __slots__ = (u'next', u'prev', u'key', u'value') def __init__(self): self.next = None @@ -651,7 +653,7 @@ def get(self, k, default=None): try: - return self._cache[k] + return self._cache[k].value except KeyError: return default @@ -881,6 +883,8 @@ This garbage collector issue have been fixed in 2.7. """ + if sys.version_info >= (2, 7): + return func def wrapper(*args, **kwargs): gcenabled = gc.isenabled() gc.disable() @@ -925,7 +929,7 @@ """ return (safehasattr(sys, "frozen") or # new py2exe safehasattr(sys, "importers") or # old py2exe - imp.is_frozen("__main__")) # tools/freeze + imp.is_frozen(u"__main__")) # tools/freeze # the location of data files matching the source code if mainfrozen() and getattr(sys, 'frozen', None) != 'macosx_app': @@ -1012,10 +1016,7 @@ proc = subprocess.Popen(cmd, shell=True, close_fds=closefds, env=env, cwd=cwd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - while True: - line = proc.stdout.readline() - if not line: - break + for line in iter(proc.stdout.readline, ''): out.write(line) proc.wait() rc = proc.returncode @@ -1214,7 +1215,7 @@ # File system features -def checkcase(path): +def fscasesensitive(path): """ Return true if the given path is on a case-sensitive filesystem @@ -1342,6 +1343,10 @@ try: posixfile(f1, 'w').close() except IOError: + try: + os.unlink(f1) + except OSError: + pass return False f2 = testfile + ".hgtmp2" @@ -1679,9 +1684,9 @@ return ''.join(buf) -def filechunkiter(f, size=65536, limit=None): +def filechunkiter(f, size=131072, limit=None): """Create a generator that produces the data in the file size - (default 65536) bytes at a time, up to optional limit (default is + (default 131072) bytes at a time, up to optional limit (default is to read all data). Chunks may be less than size bytes if the chunk is the last chunk in the file, or the file is a socket or some other type of file that sometimes reads less data than is @@ -1950,7 +1955,7 @@ except ValueError: raise Abort(_("invalid day spec: %s") % date[1:]) if days < 0: - raise Abort(_('%s must be nonnegative (see "hg help dates")') + raise Abort(_("%s must be nonnegative (see 'hg help dates')") % date[1:]) when = makedate()[0] - days * 3600 * 24 return lambda x: x >= when @@ -2294,29 +2299,8 @@ """ return _booleans.get(s.lower(), None) -_hexdig = '0123456789ABCDEFabcdef' _hextochr = dict((a + b, chr(int(a + b, 16))) - for a in _hexdig for b in _hexdig) - -def _urlunquote(s): - """Decode HTTP/HTML % encoding. - - >>> _urlunquote('abc%20def') - 'abc def' - """ - res = s.split('%') - # fastpath - if len(res) == 1: - return s - s = res[0] - for item in res[1:]: - try: - s += _hextochr[item[:2]] + item[2:] - except KeyError: - s += '%' + item - except UnicodeDecodeError: - s += unichr(int(item[:2], 16)) + item[2:] - return s + for a in string.hexdigits for b in string.hexdigits) class url(object): r"""Reliable URL parser. @@ -2374,6 +2358,22 @@ <url scheme: 'http', host: 'host', path: 'a', query: 'b', fragment: 'c'> >>> url('http://host/a?b#c', parsequery=False, parsefragment=False) <url scheme: 'http', host: 'host', path: 'a?b#c'> + + Empty path: + + >>> url('') + <url path: ''> + >>> url('#a') + <url path: '', fragment: 'a'> + >>> url('http://host/') + <url scheme: 'http', host: 'host', path: ''> + >>> url('http://host/#a') + <url scheme: 'http', host: 'host', path: '', fragment: 'a'> + + Only scheme: + + >>> url('http:') + <url scheme: 'http'> """ _safechars = "!~*'()+" @@ -2390,8 +2390,6 @@ if parsefragment and '#' in path: path, self.fragment = path.split('#', 1) - if not path: - path = None # special case for Windows drive letters and UNC paths if hasdriveletter(path) or path.startswith(r'\\'): @@ -2472,7 +2470,7 @@ 'path', 'fragment'): v = getattr(self, a) if v is not None: - setattr(self, a, _urlunquote(v)) + setattr(self, a, pycompat.urlparse.unquote(v)) def __repr__(self): attrs = []
--- a/mercurial/windows.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/windows.py Tue Oct 18 14:15:15 2016 -0500 @@ -7,7 +7,6 @@ from __future__ import absolute_import -import _winreg import errno import msvcrt import os @@ -22,6 +21,12 @@ win32, ) +try: + import _winreg as winreg + winreg.CloseKey +except ImportError: + import winreg + executablepath = win32.executablepath getuser = win32.getuser hidewindow = win32.hidewindow @@ -432,12 +437,12 @@ LOCAL_MACHINE). ''' if scope is None: - scope = (_winreg.HKEY_CURRENT_USER, _winreg.HKEY_LOCAL_MACHINE) + scope = (winreg.HKEY_CURRENT_USER, winreg.HKEY_LOCAL_MACHINE) elif not isinstance(scope, (list, tuple)): scope = (scope,) for s in scope: try: - val = _winreg.QueryValueEx(_winreg.OpenKey(s, key), valname)[0] + val = winreg.QueryValueEx(winreg.OpenKey(s, key), valname)[0] # never let a Unicode string escape into the wild return encoding.tolocal(val.encode('UTF-8')) except EnvironmentError:
--- a/mercurial/wireproto.py Tue Oct 18 14:13:06 2016 -0500 +++ b/mercurial/wireproto.py Tue Oct 18 14:15:15 2016 -0500 @@ -78,10 +78,19 @@ # """ # raise NotImplementedError() - def groupchunks(self, cg): - """return 4096 chunks from a changegroup object + def groupchunks(self, fh): + """Generator of chunks to send to the client. + + Some protocols may have compressed the contents. + """ + raise NotImplementedError() - Some protocols may have compressed the contents.""" + def compresschunks(self, chunks): + """Generator of possible compressed chunks to send to the client. + + This is like ``groupchunks()`` except it accepts a generator as + its argument. + """ raise NotImplementedError() class remotebatch(peer.batcher): @@ -187,6 +196,21 @@ .replace(':o', ',') .replace(':c', ':')) +def encodebatchcmds(req): + """Return a ``cmds`` argument value for the ``batch`` command.""" + cmds = [] + for op, argsdict in req: + # Old servers didn't properly unescape argument names. So prevent + # the sending of argument names that may not be decoded properly by + # servers. + assert all(escapearg(k) == k for k in argsdict) + + args = ','.join('%s=%s' % (escapearg(k), escapearg(v)) + for k, v in argsdict.iteritems()) + cmds.append('%s %s' % (op, args)) + + return ';'.join(cmds) + # mapping of options accepted by getbundle and their types # # Meant to be extended by extensions. It is extensions responsibility to ensure @@ -226,12 +250,7 @@ Returns an iterator of the raw responses from the server. """ - cmds = [] - for op, argsdict in req: - args = ','.join('%s=%s' % (escapearg(k), escapearg(v)) - for k, v in argsdict.iteritems()) - cmds.append('%s %s' % (op, args)) - rsp = self._callstream("batch", cmds=';'.join(cmds)) + rsp = self._callstream("batch", cmds=encodebatchcmds(req)) chunk = rsp.read(1024) work = [chunk] while chunk: @@ -399,7 +418,7 @@ else: return changegroupmod.cg1unpacker(f, 'UN') - def unbundle(self, cg, heads, source): + def unbundle(self, cg, heads, url): '''Send cg (a readable file-like object representing the changegroup to push, typically a chunkbuffer object) to the remote server as a bundle. @@ -407,7 +426,11 @@ When pushing a bundle10 stream, return an integer indicating the result of the push (see localrepository.addchangegroup()). - When pushing a bundle20 stream, return a bundle20 stream.''' + When pushing a bundle20 stream, return a bundle20 stream. + + `url` is the url the client thinks it's pushing to, which is + visible to hooks. + ''' if heads != ['force'] and self.capable('unbundlehash'): heads = encodelist(['hashed', @@ -611,7 +634,7 @@ for a in args.split(','): if a: n, v = a.split('=') - vals[n] = unescapearg(v) + vals[unescapearg(n)] = unescapearg(v) func, spec = commands[op] if spec: keys = spec.split() @@ -731,12 +754,6 @@ opts = options('debugwireargs', ['three', 'four'], others) return repo.debugwireargs(one, two, **opts) -# List of options accepted by getbundle. -# -# Meant to be extended by extensions. It is the extension's responsibility to -# ensure such options are properly processed in exchange.getbundle. -gboptslist = ['heads', 'common', 'bundlecaps'] - @wireprotocommand('getbundle', '*') def getbundle(repo, proto, others): opts = options('getbundle', gboptsmap.keys(), others) @@ -763,8 +780,8 @@ if not exchange.bundle2requested(opts.get('bundlecaps')): return ooberror(bundle2required) - cg = exchange.getbundle(repo, 'serve', **opts) - return streamres(proto.groupchunks(cg)) + chunks = exchange.getbundlechunks(repo, 'serve', **opts) + return streamres(proto.compresschunks(chunks)) @wireprotocommand('heads') def heads(repo, proto):
--- a/setup.py Tue Oct 18 14:13:06 2016 -0500 +++ b/setup.py Tue Oct 18 14:15:15 2016 -0500 @@ -318,7 +318,10 @@ if self.distribution.pure: self.distribution.ext_modules = [] elif self.distribution.cffi: - exts = [] + import setup_mpatch_cffi + import setup_bdiff_cffi + exts = [setup_mpatch_cffi.ffi.distutils_extension(), + setup_bdiff_cffi.ffi.distutils_extension()] # cffi modules go here if sys.platform == 'darwin': import setup_osutil_cffi @@ -561,7 +564,8 @@ depends=common_depends + ['mercurial/bdiff.h']), Extension('mercurial.diffhelpers', ['mercurial/diffhelpers.c'], depends=common_depends), - Extension('mercurial.mpatch', ['mercurial/mpatch.c'], + Extension('mercurial.mpatch', ['mercurial/mpatch.c', + 'mercurial/mpatch_module.c'], depends=common_depends), Extension('mercurial.parsers', ['mercurial/dirs.c', 'mercurial/manifest.c',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup_bdiff_cffi.py Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,31 @@ +from __future__ import absolute_import + +import cffi +import os + +ffi = cffi.FFI() +ffi.set_source("_bdiff_cffi", + open(os.path.join(os.path.join(os.path.dirname(__file__), 'mercurial'), + 'bdiff.c')).read(), include_dirs=['mercurial']) +ffi.cdef(""" +struct bdiff_line { + int hash, n, e; + ssize_t len; + const char *l; +}; + +struct bdiff_hunk; +struct bdiff_hunk { + int a1, a2, b1, b2; + struct bdiff_hunk *next; +}; + +int bdiff_splitlines(const char *a, ssize_t len, struct bdiff_line **lr); +int bdiff_diff(struct bdiff_line *a, int an, struct bdiff_line *b, int bn, + struct bdiff_hunk *base); +void bdiff_freehunks(struct bdiff_hunk *l); +void free(void*); +""") + +if __name__ == '__main__': + ffi.compile()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/setup_mpatch_cffi.py Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,35 @@ +from __future__ import absolute_import + +import cffi +import os + +ffi = cffi.FFI() +mpatch_c = os.path.join(os.path.join(os.path.dirname(__file__), 'mercurial', + 'mpatch.c')) +ffi.set_source("_mpatch_cffi", open(mpatch_c).read(), + include_dirs=["mercurial"]) +ffi.cdef(""" + +struct mpatch_frag { + int start, end, len; + const char *data; +}; + +struct mpatch_flist { + struct mpatch_frag *base, *head, *tail; +}; + +extern "Python" struct mpatch_flist* cffi_get_next_item(void*, ssize_t); + +int mpatch_decode(const char *bin, ssize_t len, struct mpatch_flist** res); +ssize_t mpatch_calcsize(size_t len, struct mpatch_flist *l); +void mpatch_lfree(struct mpatch_flist *a); +static int mpatch_apply(char *buf, const char *orig, size_t len, + struct mpatch_flist *l); +struct mpatch_flist *mpatch_fold(void *bins, + struct mpatch_flist* (*get_next_item)(void*, ssize_t), + ssize_t start, ssize_t end); +""") + +if __name__ == '__main__': + ffi.compile()
--- a/setup_osutil_cffi.py Tue Oct 18 14:13:06 2016 -0500 +++ b/setup_osutil_cffi.py Tue Oct 18 14:15:15 2016 -0500 @@ -45,6 +45,8 @@ ...; } attrreference_t; +typedef int ... off_t; + typedef struct val_attrs { uint32_t length; attribute_set_t returned; @@ -52,14 +54,13 @@ uint32_t obj_type; struct timespec mtime; uint32_t accessmask; - int datalength; + off_t datalength; ...; } val_attrs_t; /* the exact layout of the above struct will be figured out during build time */ typedef int ... time_t; -typedef int ... off_t; typedef struct timespec { time_t tv_sec;
--- a/tests/check-perf-code.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/check-perf-code.py Tue Oct 18 14:15:15 2016 -0500 @@ -10,6 +10,12 @@ # write static check patterns here perfpypats = [ [ + (r'(branchmap|repoview)\.subsettable', + "use getbranchmapsubsettable() for early Mercurial"), + (r'\.(vfs|svfs|opener|sopener)', + "use getvfs()/getsvfs() for early Mercurial"), + (r'ui\.configint', + "use getint() instead of ui.configint() for early Mercurial"), ], # warnings [
--- a/tests/failfilemerge.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/failfilemerge.py Tue Oct 18 14:15:15 2016 -0500 @@ -9,7 +9,7 @@ ) def failfilemerge(filemergefn, - premerge, repo, mynode, orig, fcd, fco, fca, labels=None): + premerge, repo, mynode, orig, fcd, fco, fca, labels=None): raise error.Abort("^C") return filemergefn(premerge, repo, mynode, orig, fcd, fco, fca, labels)
--- a/tests/fakemergerecord.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/fakemergerecord.py Tue Oct 18 14:15:15 2016 -0500 @@ -16,10 +16,11 @@ [('X', 'mandatory', None, 'add a fake mandatory record'), ('x', 'advisory', None, 'add a fake advisory record')], '') def fakemergerecord(ui, repo, *pats, **opts): - ms = merge.mergestate.read(repo) - records = ms._makerecords() - if opts.get('mandatory'): - records.append(('X', 'mandatory record')) - if opts.get('advisory'): - records.append(('x', 'advisory record')) - ms._writerecords(records) + with repo.wlock(): + ms = merge.mergestate.read(repo) + records = ms._makerecords() + if opts.get('mandatory'): + records.append(('X', 'mandatory record')) + if opts.get('advisory'): + records.append(('x', 'advisory record')) + ms._writerecords(records)
--- a/tests/hghave.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/hghave.py Tue Oct 18 14:15:15 2016 -0500 @@ -110,8 +110,13 @@ def has_bzr(): try: import bzrlib + import bzrlib.bzrdir + import bzrlib.errors + import bzrlib.revision + import bzrlib.revisionspec + bzrlib.revisionspec.RevisionSpec return bzrlib.__doc__ is not None - except ImportError: + except (AttributeError, ImportError): return False @checkvers("bzr", "Canonical's Bazaar client >= %s", (1.14,)) @@ -349,6 +354,14 @@ def has_gpg(): return matchoutput('gpg --version 2>&1', br'GnuPG') +@check("gpg2", "gpg client v2") +def has_gpg2(): + return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.') + +@check("gpg21", "gpg client v2.1+") +def has_gpg21(): + return matchoutput('gpg --version 2>&1', br'GnuPG[^0-9]+2\.(?!0)') + @check("unix-permissions", "unix-style permissions") def has_unix_permissions(): d = tempfile.mkdtemp(dir='.', prefix=tempprefix) @@ -522,6 +535,10 @@ br'other supported Python versions') return dpkg and dh and dh_py2 +@check("demandimport", "demandimport enabled") +def has_demandimport(): + return os.environ.get('HGDEMANDIMPORT') != 'disable' + @check("absimport", "absolute_import in __future__") def has_absimport(): import __future__ @@ -540,6 +557,16 @@ def has_python3exe(): return 'PYTHON3' in os.environ +@check("py3pygments", "Pygments available on Python 3.x") +def has_py3pygments(): + if has_py3k(): + return has_pygments() + elif has_python3exe(): + # just check exit status (ignoring output) + py3 = os.environ['PYTHON3'] + return matchoutput('%s -c "import pygments"' % py3, br'') + return False + @check("pure", "running with pure Python code") def has_pure(): return any([ @@ -559,3 +586,7 @@ return True except ImportError: return False + +@check("unziplinks", "unzip(1) understands and extracts symlinks") +def unzip_understands_symlinks(): + return matchoutput('unzip --help', br'Info-ZIP')
--- a/tests/killdaemons.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/killdaemons.py Tue Oct 18 14:15:15 2016 -0500 @@ -82,7 +82,11 @@ for line in fp: try: pid = int(line) + if pid <= 0: + raise ValueError except ValueError: + logfn('# Not killing daemon process %s - invalid pid' + % line.rstrip()) continue kill(pid, logfn, tryhard) fp.close()
--- a/tests/lockdelay.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/lockdelay.py Tue Oct 18 14:15:15 2016 -0500 @@ -7,20 +7,16 @@ import os import time -from mercurial import ( - lock as lockmod, -) +def reposetup(ui, repo): -class delaylock(lockmod.lock): - def lock(self): - delay = float(os.environ.get('HGPRELOCKDELAY', '0.0')) - if delay: - time.sleep(delay) - res = super(delaylock, self).lock() - delay = float(os.environ.get('HGPOSTLOCKDELAY', '0.0')) - if delay: - time.sleep(delay) - return res - -def extsetup(ui): - lockmod.lock = delaylock + class delayedlockrepo(repo.__class__): + def lock(self): + delay = float(os.environ.get('HGPRELOCKDELAY', '0.0')) + if delay: + time.sleep(delay) + res = super(delayedlockrepo, self).lock() + delay = float(os.environ.get('HGPOSTLOCKDELAY', '0.0')) + if delay: + time.sleep(delay) + return res + repo.__class__ = delayedlockrepo
--- a/tests/md5sum.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/md5sum.py Tue Oct 18 14:15:15 2016 -0500 @@ -34,10 +34,7 @@ m = md5() try: - while True: - data = fp.read(8192) - if not data: - break + for data in iter(lambda: fp.read(8192), ''): m.update(data) except IOError as msg: sys.stderr.write('%s: I/O error: %s\n' % (filename, msg))
--- a/tests/test-acl.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-acl.t Tue Oct 18 14:15:15 2016 -0500 @@ -44,13 +44,6 @@ > EOF > } - $ cat << EOF >> $HGRCPATH - > [experimental] - > # drop me once bundle2 is the default, - > # added to get test change early. - > bundle2-exp = True - > EOF - $ hg init a $ cd a $ mkdir foo foo/Bar quux @@ -119,11 +112,11 @@ adding foo/file.txt revisions adding quux/file.py revisions added 3 changesets with 3 changes to 3 files - bundle2-input-part: total payload size 1606 + updating the branch cache + bundle2-input-part: total payload size 1553 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-bundle: 3 parts total - updating the branch cache bundle2-output-bundle: "HG20", 2 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -184,11 +177,11 @@ added 3 changesets with 3 changes to 3 files calling hook pretxnchangegroup.acl: hgext.acl.hook acl: changes have source "push" - skipping - bundle2-input-part: total payload size 1606 + updating the branch cache + bundle2-input-part: total payload size 1553 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-bundle: 3 parts total - updating the branch cache bundle2-output-bundle: "HG20", 2 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -260,11 +253,11 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" acl: path access granted: "911600dab2ae" - bundle2-input-part: total payload size 1606 + updating the branch cache + bundle2-input-part: total payload size 1553 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-bundle: 3 parts total - updating the branch cache bundle2-output-bundle: "HG20", 2 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -332,7 +325,7 @@ acl: acl.deny not enabled acl: branch access granted: "ef1ea85a6374" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -401,7 +394,7 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -467,7 +460,7 @@ acl: acl.deny enabled, 0 entries for user barney acl: branch access granted: "ef1ea85a6374" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -538,7 +531,7 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "fred" not allowed on "quux/file.py" (changeset "911600dab2ae") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -608,7 +601,7 @@ acl: path access granted: "ef1ea85a6374" acl: branch access granted: "f9cafe1212c8" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -675,7 +668,7 @@ acl: acl.deny enabled, 0 entries for user barney acl: branch access granted: "ef1ea85a6374" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "barney" not allowed on "foo/file.txt" (changeset "ef1ea85a6374") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -750,11 +743,11 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" acl: path access granted: "911600dab2ae" - bundle2-input-part: total payload size 1606 + updating the branch cache + bundle2-input-part: total payload size 1553 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-bundle: 3 parts total - updating the branch cache bundle2-output-bundle: "HG20", 2 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -833,7 +826,7 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -902,7 +895,7 @@ calling hook pretxnchangegroup.acl: hgext.acl.hook acl: checking access for user "barney" error: pretxnchangegroup.acl hook raised an exception: [Errno 2] No such file or directory: '../acl.config' - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -984,7 +977,7 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "betty" not allowed on "quux/file.py" (changeset "911600dab2ae") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -1068,11 +1061,11 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" acl: path access granted: "911600dab2ae" - bundle2-input-part: total payload size 1606 + updating the branch cache + bundle2-input-part: total payload size 1553 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-bundle: 3 parts total - updating the branch cache bundle2-output-bundle: "HG20", 2 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -1154,11 +1147,11 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" acl: path access granted: "911600dab2ae" - bundle2-input-part: total payload size 1606 + updating the branch cache + bundle2-input-part: total payload size 1553 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-bundle: 3 parts total - updating the branch cache bundle2-output-bundle: "HG20", 2 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -1234,7 +1227,7 @@ acl: path access granted: "ef1ea85a6374" acl: branch access granted: "f9cafe1212c8" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -1313,11 +1306,11 @@ acl: path access granted: "f9cafe1212c8" acl: branch access granted: "911600dab2ae" on branch "default" acl: path access granted: "911600dab2ae" - bundle2-input-part: total payload size 1606 + updating the branch cache + bundle2-input-part: total payload size 1553 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-bundle: 3 parts total - updating the branch cache bundle2-output-bundle: "HG20", 2 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -1395,7 +1388,7 @@ acl: path access granted: "ef1ea85a6374" acl: branch access granted: "f9cafe1212c8" on branch "default" error: pretxnchangegroup.acl hook failed: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8") - bundle2-input-part: total payload size 1606 + bundle2-input-part: total payload size 1553 bundle2-input-bundle: 3 parts total transaction abort! rollback completed @@ -1515,13 +1508,13 @@ acl: path access granted: "911600dab2ae" acl: branch access granted: "e8fc755d4d82" on branch "foobar" acl: path access granted: "e8fc755d4d82" - bundle2-input-part: total payload size 2101 + updating the branch cache + bundle2-input-part: total payload size 2068 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01" bundle2-input-bundle: 4 parts total - updating the branch cache bundle2-output-bundle: "HG20", 3 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -1602,7 +1595,7 @@ acl: branch access granted: "911600dab2ae" on branch "default" acl: path access granted: "911600dab2ae" error: pretxnchangegroup.acl hook failed: acl: user "astro" denied on branch "foobar" (changeset "e8fc755d4d82") - bundle2-input-part: total payload size 2101 + bundle2-input-part: total payload size 2068 bundle2-input-bundle: 4 parts total transaction abort! rollback completed @@ -1670,7 +1663,7 @@ acl: acl.allow not enabled acl: acl.deny not enabled error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374") - bundle2-input-part: total payload size 2101 + bundle2-input-part: total payload size 2068 bundle2-input-bundle: 4 parts total transaction abort! rollback completed @@ -1740,7 +1733,7 @@ acl: acl.allow not enabled acl: acl.deny not enabled error: pretxnchangegroup.acl hook failed: acl: user "astro" not allowed on branch "default" (changeset "ef1ea85a6374") - bundle2-input-part: total payload size 2101 + bundle2-input-part: total payload size 2068 bundle2-input-bundle: 4 parts total transaction abort! rollback completed @@ -1811,13 +1804,13 @@ acl: path access granted: "911600dab2ae" acl: branch access granted: "e8fc755d4d82" on branch "foobar" acl: path access granted: "e8fc755d4d82" - bundle2-input-part: total payload size 2101 + updating the branch cache + bundle2-input-part: total payload size 2068 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01" bundle2-input-bundle: 4 parts total - updating the branch cache bundle2-output-bundle: "HG20", 3 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -1904,13 +1897,13 @@ acl: path access granted: "911600dab2ae" acl: branch access granted: "e8fc755d4d82" on branch "foobar" acl: path access granted: "e8fc755d4d82" - bundle2-input-part: total payload size 2101 + updating the branch cache + bundle2-input-part: total payload size 2068 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01" bundle2-input-bundle: 4 parts total - updating the branch cache bundle2-output-bundle: "HG20", 3 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -1989,7 +1982,7 @@ acl: acl.allow not enabled acl: acl.deny not enabled error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374") - bundle2-input-part: total payload size 2101 + bundle2-input-part: total payload size 2068 bundle2-input-bundle: 4 parts total transaction abort! rollback completed @@ -2065,13 +2058,13 @@ acl: path access granted: "911600dab2ae" acl: branch access granted: "e8fc755d4d82" on branch "foobar" acl: path access granted: "e8fc755d4d82" - bundle2-input-part: total payload size 2101 + updating the branch cache + bundle2-input-part: total payload size 2068 bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:911600dab2ae7a9baff75958b84fe606851ce955" bundle2-input-part: "pushkey" (params: 4 mandatory) supported pushing key for "phases:e8fc755d4d8217ee5b0c2bb41558c40d43b92c01" bundle2-input-bundle: 4 parts total - updating the branch cache bundle2-output-bundle: "HG20", 3 parts total bundle2-output-part: "reply:changegroup" (advisory) (params: 0 advisory) empty payload bundle2-output-part: "reply:pushkey" (params: 0 advisory) empty payload @@ -2144,7 +2137,7 @@ acl: acl.allow not enabled acl: acl.deny not enabled error: pretxnchangegroup.acl hook failed: acl: user "george" denied on branch "default" (changeset "ef1ea85a6374") - bundle2-input-part: total payload size 2101 + bundle2-input-part: total payload size 2068 bundle2-input-bundle: 4 parts total transaction abort! rollback completed
--- a/tests/test-alias.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-alias.t Tue Oct 18 14:15:15 2016 -0500 @@ -99,7 +99,7 @@ patchbomb command to send changesets as (a series of) patch emails - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) no definition @@ -441,7 +441,7 @@ alias for: hg root - (use "hg rt -h" to show more help) + (use 'hg rt -h' to show more help) [255] invalid global arguments for normal commands, aliases, and shell aliases @@ -470,7 +470,7 @@ summary summarize working directory state update update working directory (or switch revisions) - (use "hg help" for the full list of commands or "hg -v" for details) + (use 'hg help' for the full list of commands or 'hg -v' for details) [255] $ hg --invalid mylog hg: option --invalid not recognized @@ -496,7 +496,7 @@ summary summarize working directory state update update working directory (or switch revisions) - (use "hg help" for the full list of commands or "hg -v" for details) + (use 'hg help' for the full list of commands or 'hg -v' for details) [255] $ hg --invalid blank hg: option --invalid not recognized @@ -522,7 +522,7 @@ summary summarize working directory state update update working directory (or switch revisions) - (use "hg help" for the full list of commands or "hg -v" for details) + (use 'hg help' for the full list of commands or 'hg -v' for details) [255] environment variable changes in alias commands
--- a/tests/test-archive-symlinks.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-archive-symlinks.t Tue Oct 18 14:15:15 2016 -0500 @@ -29,6 +29,7 @@ $ readlink.py dangling dangling -> nothing +#if unziplinks zip $ cd "$origdir" @@ -36,5 +37,6 @@ $ cd zip $ readlink.py dangling dangling -> nothing +#endif $ cd ..
--- a/tests/test-bad-extension.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bad-extension.t Tue Oct 18 14:15:15 2016 -0500 @@ -53,7 +53,7 @@ *** failed to import extension badext from $TESTTMP/badext.py: bit bucket overflow Traceback (most recent call last): Exception: bit bucket overflow - could not import hgext.badext2 (No module named *badext2): trying badext2 (glob) + could not import hgext.badext2 (No module named *badext2): trying hgext3rd.badext2 (glob) Traceback (most recent call last): ImportError: No module named *badext2 (glob) could not import hgext3rd.badext2 (No module named *badext2): trying badext2 (glob)
--- a/tests/test-blackbox.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-blackbox.t Tue Oct 18 14:15:15 2016 -0500 @@ -4,6 +4,8 @@ > blackbox= > mock=$TESTDIR/mockblackbox.py > mq= + > [alias] + > confuse = log --limit 3 > EOF $ hg init blackboxtest $ cd blackboxtest @@ -17,6 +19,18 @@ 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a exited 0 after * seconds (glob) 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox +alias expansion is logged + $ hg confuse + $ hg blackbox + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> add a exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000+ (5000)> blackbox --config blackbox.dirty=True exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> confuse + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> alias 'confuse' expands to 'log --limit 3' + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> confuse exited 0 after * seconds (glob) + 1970/01/01 00:00:00 bob @0000000000000000000000000000000000000000 (5000)> blackbox + incoming change tracking create two heads to verify that we only see one change in the log later
--- a/tests/test-bookmarks-pushpull.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bookmarks-pushpull.t Tue Oct 18 14:15:15 2016 -0500 @@ -7,9 +7,6 @@ > publish=False > [experimental] > evolution=createmarkers,exchange - > # drop me once bundle2 is the default, - > # added to get test change early. - > bundle2-exp = True > EOF initialize @@ -430,7 +427,7 @@ pushing to http://localhost:$HGPORT2/ searching for changes abort: push creates new remote head c922c0139ca0 with bookmark 'Y'! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg -R ../a book @ 1:0d2164f0ce0d @@ -446,7 +443,7 @@ pushing to http://localhost:$HGPORT2/ searching for changes abort: push creates new remote head c922c0139ca0 with bookmark 'Y'! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg -R ../a book @ 1:0d2164f0ce0d @@ -736,7 +733,7 @@ searching for changes remote has heads on branch 'default' that are not known locally: a2a606d9ff1b abort: push creates new remote head 54694f811df9 with bookmark 'X'! - (pull and merge or see "hg help push" for details about pushing new heads) + (pull and merge or see 'hg help push' for details about pushing new heads) [255] $ cd ../addmarks @@ -825,7 +822,7 @@ Using ssh --------- - $ hg push -B @ ssh --config experimental.bundle2-exp=True + $ hg push -B @ ssh # bundle2+ pushing to ssh://user@dummy/issue4455-dest searching for changes no changes found @@ -835,7 +832,7 @@ $ hg -R ../issue4455-dest/ bookmarks no bookmarks set - $ hg push -B @ ssh --config experimental.bundle2-exp=False + $ hg push -B @ ssh --config devel.legacy.exchange=bundle1 pushing to ssh://user@dummy/issue4455-dest searching for changes no changes found @@ -848,7 +845,7 @@ Using http ---------- - $ hg push -B @ http --config experimental.bundle2-exp=True + $ hg push -B @ http # bundle2+ pushing to http://localhost:$HGPORT/ searching for changes no changes found @@ -858,7 +855,7 @@ $ hg -R ../issue4455-dest/ bookmarks no bookmarks set - $ hg push -B @ http --config experimental.bundle2-exp=False + $ hg push -B @ http --config devel.legacy.exchange=bundle1 pushing to http://localhost:$HGPORT/ searching for changes no changes found
--- a/tests/test-branches.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-branches.t Tue Oct 18 14:15:15 2016 -0500 @@ -516,6 +516,8 @@ } ] + $ hg branches --closed -T '{if(closed, "{branch}\n")}' + c Tests of revision branch name caching @@ -554,6 +556,18 @@ $ rmdir .hg/cache/rbc-revs-v1 $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1 +no errors when wlock cannot be acquired + +#if unix-permissions + $ mv .hg/cache/rbc-revs-v1 .hg/cache/rbc-revs-v1_ + $ rm -f .hg/cache/branch* + $ chmod 555 .hg + $ hg head a -T '{rev}\n' + 5 + $ chmod 755 .hg + $ mv .hg/cache/rbc-revs-v1_ .hg/cache/rbc-revs-v1 +#endif + recovery from invalid cache revs file with trailing data $ echo >> .hg/cache/rbc-revs-v1 $ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
--- a/tests/test-bundle-type.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bundle-type.t Tue Oct 18 14:15:15 2016 -0500 @@ -123,6 +123,6 @@ $ cd t1 $ hg bundle -a -t garbage ../bgarbage abort: garbage is not a recognized bundle specification - (see "hg help bundle" for supported values for --type) + (see 'hg help bundle' for supported values for --type) [255] $ cd ..
--- a/tests/test-bundle.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bundle.t Tue Oct 18 14:15:15 2016 -0500 @@ -279,26 +279,26 @@ $ hg debugbundle --spec packed.hg none-packed1;requirements%3Dgeneraldelta%2Crevlogv1 -generaldelta requirement is listed in stream clone bundles +generaldelta requirement is not listed in stream clone bundles unless used - $ hg --config format.generaldelta=true init testgd - $ cd testgd + $ hg --config format.usegeneraldelta=false init testnongd + $ cd testnongd $ touch foo $ hg -q commit -A -m initial $ cd .. - $ hg -R testgd debugcreatestreamclonebundle packedgd.hg + $ hg -R testnongd debugcreatestreamclonebundle packednongd.hg writing 301 bytes for 3 files - bundle requirements: generaldelta, revlogv1 + bundle requirements: revlogv1 - $ f -B 64 --size --sha1 --hexdump packedgd.hg - packedgd.hg: size=396, sha1=981f9e589799335304a5a9a44caa3623a48d2a9f + $ f -B 64 --size --sha1 --hexdump packednongd.hg + packednongd.hg: size=383, sha1=1d9c230238edd5d38907100b729ba72b1831fe6f 0000: 48 47 53 31 55 4e 00 00 00 00 00 00 00 03 00 00 |HGS1UN..........| - 0010: 00 00 00 00 01 2d 00 16 67 65 6e 65 72 61 6c 64 |.....-..generald| - 0020: 65 6c 74 61 2c 72 65 76 6c 6f 67 76 31 00 64 61 |elta,revlogv1.da| - 0030: 74 61 2f 66 6f 6f 2e 69 00 36 34 0a 00 03 00 01 |ta/foo.i.64.....| + 0010: 00 00 00 00 01 2d 00 09 72 65 76 6c 6f 67 76 31 |.....-..revlogv1| + 0020: 00 64 61 74 61 2f 66 6f 6f 2e 69 00 36 34 0a 00 |.data/foo.i.64..| + 0030: 01 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 |................| - $ hg debugbundle --spec packedgd.hg - none-packed1;requirements%3Dgeneraldelta%2Crevlogv1 + $ hg debugbundle --spec packednongd.hg + none-packed1;requirements%3Drevlogv1 Unpacking packed1 bundles with "hg unbundle" isn't allowed @@ -310,9 +310,51 @@ packed1 can be consumed from debug command +(this also confirms that streamclone-ed changes are visible via +@filecache properties to in-process procedures before closing +transaction) + + $ cat > $TESTTMP/showtip.py <<EOF + > from __future__ import absolute_import + > + > def showtip(ui, repo, hooktype, **kwargs): + > ui.warn('%s: %s\n' % (hooktype, repo['tip'].hex()[:12])) + > + > def reposetup(ui, repo): + > # this confirms (and ensures) that (empty) 00changelog.i + > # before streamclone is already cached as repo.changelog + > ui.setconfig('hooks', 'pretxnopen.showtip', showtip) + > + > # this confirms that streamclone-ed changes are visible to + > # in-process procedures before closing transaction + > ui.setconfig('hooks', 'pretxnclose.showtip', showtip) + > + > # this confirms that streamclone-ed changes are still visible + > # after closing transaction + > ui.setconfig('hooks', 'txnclose.showtip', showtip) + > EOF + $ cat >> $HGRCPATH <<EOF + > [extensions] + > showtip = $TESTTMP/showtip.py + > EOF + $ hg -R packed debugapplystreamclonebundle packed.hg 6 files to transfer, 2.60 KB of data + pretxnopen: 000000000000 + pretxnclose: aa35859c02ea transferred 2.60 KB in *.* seconds (* */sec) (glob) + txnclose: aa35859c02ea + +(for safety, confirm visibility of streamclone-ed changes by another +process, too) + + $ hg -R packed tip -T "{node|short}\n" + aa35859c02ea + + $ cat >> $HGRCPATH <<EOF + > [extensions] + > showtip = ! + > EOF Does not work on non-empty repo
--- a/tests/test-bundle2-exchange.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bundle2-exchange.t Tue Oct 18 14:15:15 2016 -0500 @@ -16,7 +16,6 @@ $ cat >> $HGRCPATH << EOF > [experimental] > evolution=createmarkers,exchange - > bundle2-exp=True > bundle2-output-capture=True > [ui] > ssh=python "$TESTDIR/dummyssh" @@ -970,7 +969,7 @@ $ hg serve -p $HGPORT -d --pid-file=hg.pid $ cat hg.pid >> $DAEMON_PIDS - $ hg --config experimental.bundle2-exp=false clone http://localhost:$HGPORT/ not-bundle2 + $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2 requesting all changes abort: remote error: incompatible Mercurial client; bundle2 required @@ -993,7 +992,7 @@ $ hg serve -p $HGPORT -d --pid-file=hg.pid $ cat hg.pid >> $DAEMON_PIDS - $ hg --config experimental.bundle2-exp=false clone http://localhost:$HGPORT/ not-bundle2-1 + $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-1 requesting all changes adding changesets adding manifests @@ -1014,7 +1013,7 @@ $ hg serve -p $HGPORT -d --pid-file=hg.pid $ cat hg.pid >> $DAEMON_PIDS - $ hg --config experimental.bundle2-exp=false clone http://localhost:$HGPORT/ not-bundle2 + $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2 requesting all changes abort: remote error: incompatible Mercurial client; bundle2 required @@ -1031,7 +1030,7 @@ > EOF $ hg serve -p $HGPORT -d --pid-file=hg.pid $ cat hg.pid >> $DAEMON_PIDS - $ hg --config experimental.bundle2-exp=false clone http://localhost:$HGPORT not-bundle2 + $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT not-bundle2 requesting all changes abort: remote error: incompatible Mercurial client; bundle2 required @@ -1046,7 +1045,7 @@ $ hg serve -p $HGPORT -d --pid-file=hg.pid $ cat hg.pid >> $DAEMON_PIDS - $ hg --config experimental.bundle2-exp=false clone http://localhost:$HGPORT/ not-bundle2 + $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2 requesting all changes abort: remote error: incompatible Mercurial client; bundle2 required @@ -1063,7 +1062,7 @@ $ hg serve -p $HGPORT -d --pid-file=hg.pid $ cat hg.pid >> $DAEMON_PIDS - $ hg --config experimental.bundle2-exp=false clone http://localhost:$HGPORT/ not-bundle2-2 + $ hg --config devel.legacy.exchange=bundle1 clone http://localhost:$HGPORT/ not-bundle2-2 requesting all changes adding changesets adding manifests @@ -1100,7 +1099,7 @@ $ cd bundle2-only $ echo commit > foo $ hg commit -m commit - $ hg --config experimental.bundle2-exp=false push + $ hg --config devel.legacy.exchange=bundle1 push pushing to http://localhost:$HGPORT/ searching for changes abort: remote error:
--- a/tests/test-bundle2-format.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bundle2-format.t Tue Oct 18 14:15:15 2016 -0500 @@ -112,7 +112,7 @@ > bundled = repo.revs('%ld::%ld', revs, revs) > headmissing = [c.node() for c in repo.set('heads(%ld)', revs)] > headcommon = [c.node() for c in repo.set('parents(%ld) - %ld', revs, revs)] - > outgoing = discovery.outgoing(repo.changelog, headcommon, headmissing) + > outgoing = discovery.outgoing(repo, headcommon, headmissing) > cg = changegroup.getlocalchangegroup(repo, 'test:bundle2', outgoing, None) > bundler.newpart('changegroup', data=cg.getchunks(), > mandatory=False) @@ -227,7 +227,6 @@ > [extensions] > bundle2=$TESTTMP/bundle2.py > [experimental] - > bundle2-exp=True > evolution=createmarkers > [ui] > ssh=python "$TESTDIR/dummyssh"
--- a/tests/test-bundle2-multiple-changegroups.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bundle2-multiple-changegroups.t Tue Oct 18 14:15:15 2016 -0500 @@ -3,7 +3,7 @@ $ cat > bundle2.py <<EOF > """ > """ - > from mercurial import changegroup, exchange + > from mercurial import changegroup, discovery, exchange > > def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None, > b2caps=None, heads=None, common=None, @@ -12,13 +12,14 @@ > # changegroup part we are being requested. Use the parent of each head > # in 'heads' as intermediate heads for the first changegroup. > intermediates = [repo[r].p1().node() for r in heads] - > cg = changegroup.getchangegroup(repo, source, heads=intermediates, - > common=common, bundlecaps=bundlecaps) + > outgoing = discovery.outgoing(repo, common, intermediates) + > cg = changegroup.getchangegroup(repo, source, outgoing, + > bundlecaps=bundlecaps) > bundler.newpart('output', data='changegroup1') > bundler.newpart('changegroup', data=cg.getchunks()) - > cg = changegroup.getchangegroup(repo, source, heads=heads, - > common=common + intermediates, - > bundlecaps=bundlecaps) + > outgoing = discovery.outgoing(repo, common + intermediates, heads) + > cg = changegroup.getchangegroup(repo, source, outgoing, + > bundlecaps=bundlecaps) > bundler.newpart('output', data='changegroup2') > bundler.newpart('changegroup', data=cg.getchunks()) > @@ -33,8 +34,6 @@ > EOF $ cat >> $HGRCPATH << EOF - > [experimental] - > bundle2-exp=True > [ui] > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline} > EOF
--- a/tests/test-bundle2-pushback.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bundle2-pushback.t Tue Oct 18 14:15:15 2016 -0500 @@ -50,8 +50,6 @@ $ cat >> $HGRCPATH <<EOF > [extensions] > bundle2=$TESTTMP/bundle2.py - > [experimental] - > bundle2-exp = True > EOF Without config
--- a/tests/test-bundle2-remote-changegroup.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-bundle2-remote-changegroup.t Tue Oct 18 14:15:15 2016 -0500 @@ -8,7 +8,7 @@ > Current bundle2 implementation doesn't provide a way to generate those > parts, so they must be created by extensions. > """ - > from mercurial import bundle2, changegroup, exchange, util + > from mercurial import bundle2, changegroup, discovery, exchange, util > > def _getbundlechangegrouppart(bundler, repo, source, bundlecaps=None, > b2caps=None, heads=None, common=None, @@ -22,7 +22,7 @@ > part to add: > - changegroup common_revset heads_revset > Creates a changegroup part based, using common_revset and - > heads_revset for changegroup.getchangegroup. + > heads_revset for outgoing > - remote-changegroup url file > Creates a remote-changegroup part for a bundle at the given > url. Size and digest, as required by the client, are computed @@ -63,8 +63,8 @@ > _common, heads = args.split() > common.extend(repo.lookup(r) for r in repo.revs(_common)) > heads = [repo.lookup(r) for r in repo.revs(heads)] - > cg = changegroup.getchangegroup(repo, 'changegroup', - > heads=heads, common=common) + > outgoing = discovery.outgoing(repo, common, heads) + > cg = changegroup.getchangegroup(repo, 'changegroup', outgoing) > newpart('changegroup', cg.getchunks()) > else: > raise Exception('unknown verb') @@ -78,8 +78,6 @@ $ cat dumb.pid >> $DAEMON_PIDS $ cat >> $HGRCPATH << EOF - > [experimental] - > bundle2-exp=True > [ui] > ssh=python "$TESTDIR/dummyssh" > logtemplate={rev}:{node|short} {phase} {author} {bookmarks} {desc|firstline}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-check-py3-commands.t Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,14 @@ +#require py3exe + +This test helps in keeping a track on which commands we can run on +Python 3 and see what kind of errors are coming up. +The full traceback is hidden to have a stable output. + + $ for cmd in version debuginstall ; do + > echo $cmd + > $PYTHON3 `which hg` $cmd 2>&1 2>&1 | tail -1 + > done + version + TypeError: str expected, not bytes + debuginstall + TypeError: str expected, not bytes
--- a/tests/test-check-py3-compat.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-check-py3-compat.t Tue Oct 18 14:15:15 2016 -0500 @@ -13,164 +13,22 @@ tests/test-demandimport.py not using absolute_import #if py3exe - $ hg files 'set:(**.py)' | sed 's|\\|/|g' | xargs $PYTHON3 contrib/check-py3-compat.py - doc/hgmanpage.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) - hgext/acl.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/automv.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/blackbox.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/bugzilla.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/censor.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/chgserver.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/children.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/churn.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/clonebundles.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/color.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) - hgext/convert/bzr.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/common.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/convcmd.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/cvs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/cvsps.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/darcs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/filemap.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/git.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/gnuarch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/hg.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/monotone.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/p4.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/subversion.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/convert/transport.py: error importing module: <ImportError> No module named 'svn.client' (line *) (glob) - hgext/eol.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/extdiff.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/factotum.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/fetch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/fsmonitor/state.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/fsmonitor/watchmanclient.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/gpg.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/graphlog.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/hgk.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/highlight/highlight.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/histedit.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/journal.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/keyword.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/basestore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/lfcommands.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/lfutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/localstore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/overrides.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/proto.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/remotestore.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/reposetup.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/storefactory.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/largefiles/uisetup.py: error importing: <TypeError> str expected, not bytes (error at encoding.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: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/notify.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/pager.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/patchbomb.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/purge.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/rebase.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/record.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/relink.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/schemes.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/share.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/shelve.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/strip.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/transplant.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/win32mbcs.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - hgext/win32text.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/archival.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) - mercurial/bookmarks.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/branchmap.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/bundle2.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) - mercurial/bundlerepo.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/byterange.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/changegroup.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/changelog.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/cmdutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/commands.py: invalid syntax: invalid syntax (<unknown>, line *) (glob) - mercurial/commandserver.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/config.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/context.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/copies.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/crecord.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/dagparser.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/dagutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/destutil.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/dirstate.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/discovery.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/dispatch.py: error importing: <TypeError> str expected, not bytes (error at encoding.py:*) (glob) - mercurial/exchange.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/extensions.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/fancyopts.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/filelog.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/filemerge.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/fileset.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/formatter.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/graphmod.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/hbisect.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/help.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/hg.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/hgweb/common.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) - mercurial/hgweb/hgweb_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) - mercurial/hgweb/hgwebdir_mod.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) - mercurial/hgweb/protocol.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) - mercurial/hgweb/request.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) - mercurial/hgweb/server.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) - mercurial/hgweb/webcommands.py: error importing module: <SystemError> Parent module 'mercurial.hgweb' not loaded, cannot perform relative import (line *) (glob) - 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: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/httpconnection.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/httppeer.py: error importing: <TypeError> str expected, not bytes (error at i18n.py:*) (glob) - mercurial/keepalive.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/localrepo.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/lock.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/mail.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/manifest.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob) - mercurial/match.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/mdiff.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob) - mercurial/merge.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/minirst.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/namespaces.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/obsolete.py: error importing: <TypeError> getattr(): attribute name must be string (error at pycompat.py:*) (glob) - mercurial/patch.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/pathutil.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/peer.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/pure/mpatch.py: error importing module: <AttributeError> 'VendorImporter' object has no attribute 'find_spec' (line *) (glob) - mercurial/pure/osutil.py: error importing module: <AttributeError> 'VendorImporter' object has no attribute 'find_spec' (line *) (glob) - mercurial/pure/parsers.py: error importing module: <AttributeError> 'VendorImporter' object has no attribute 'find_spec' (line *) (glob) - mercurial/pushkey.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/pvec.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/registrar.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/repair.py: error importing module: <SyntaxError> invalid syntax (bundle2.py, line *) (line *) (glob) - mercurial/repoview.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/revlog.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/revset.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/scmposix.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/scmutil.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/scmwindows.py: error importing module: <ImportError> No module named '_winreg' (line *) (glob) - mercurial/similar.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/simplemerge.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/sshpeer.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/sshserver.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/sslutil.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/statichttprepo.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/store.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/streamclone.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/subrepo.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/tagmerge.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/tags.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/templatefilters.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/templatekw.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/templater.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/transaction.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/ui.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/unionrepo.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/url.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/util.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/verify.py: error importing: <TypeError> '_fields_' must be a sequence of (name, C type) pairs (error at osutil.py:*) (glob) - mercurial/win32.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 (bundle2.py, line *) (line *) (glob) + $ hg files 'set:(**.py) - grep(pygments)' | sed 's|\\|/|g' \ + > | xargs $PYTHON3 contrib/check-py3-compat.py \ + > | sed 's/[0-9][0-9]*)$/*)/' + hgext/convert/transport.py: error importing: <ImportError> No module named 'svn.client' (error at transport.py:*) + hgext/fsmonitor/pywatchman/capabilities.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*) + hgext/fsmonitor/pywatchman/pybser.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*) + hgext/fsmonitor/watchmanclient.py: error importing: <ImportError> No module named 'pybser' (error at __init__.py:*) + hgext/mq.py: error importing: <TypeError> __import__() argument 1 must be str, not bytes (error at extensions.py:*) + mercurial/scmwindows.py: error importing: <ImportError> No module named 'winreg' (error at scmwindows.py:*) + mercurial/win32.py: error importing: <ImportError> No module named 'msvcrt' (error at win32.py:*) + mercurial/windows.py: error importing: <ImportError> No module named 'msvcrt' (error at windows.py:*) #endif + +#if py3exe py3pygments + $ hg files 'set:(**.py) and grep(pygments)' | sed 's|\\|/|g' \ + > | xargs $PYTHON3 contrib/check-py3-compat.py \ + > | sed 's/[0-9][0-9]*)$/*)/' +#endif
--- a/tests/test-clone.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-clone.t Tue Oct 18 14:15:15 2016 -0500 @@ -829,7 +829,7 @@ Default path should be source, not share. $ hg -R share-dest1b config paths.default - $TESTTMP/source1a (glob) + $TESTTMP/source1b (glob) Checked out revision should be head of default branch @@ -1060,7 +1060,12 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: 1a - $ cat race1.log +One repo should be new, the other should be shared from the pool. We +don't care which is which, so we just make sure we always print the +one containing "new pooled" first, then one one containing "existing +pooled". + + $ (grep 'new pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock (sharing from new pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1) requesting all changes adding changesets @@ -1073,10 +1078,8 @@ updating working directory 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cat race2.log + $ (grep 'existing pooled' race1.log > /dev/null && cat race1.log || cat race2.log) | grep -v lock (sharing from existing pooled repository b5f04eac9d8f7a6a9fcb070243cccea7dc5ea0c1) - waiting for lock on repository share-destrace2 held by * (glob) - got lock after \d+ seconds (re) searching for changes no changes found adding remote bookmark bookA
--- a/tests/test-clonebundles.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-clonebundles.t Tue Oct 18 14:15:15 2016 -0500 @@ -61,7 +61,7 @@ $ echo "http://localhost:$HGPORT1/bundle.hg" > server/.hg/clonebundles.manifest $ hg clone http://localhost:$HGPORT server-not-runner applying clone bundle from http://localhost:$HGPORT1/bundle.hg - error fetching bundle: * refused* (glob) + error fetching bundle: (.* refused.*|Protocol not supported) (re) abort: error applying bundle (if this error persists, consider contacting the server operator or disable clone bundles via "--config ui.clonebundles=false") [255] @@ -156,34 +156,32 @@ by old clients. $ f --size --hexdump full.hg - full.hg: size=418 + full.hg: size=396 0000: 48 47 32 30 00 00 00 0e 43 6f 6d 70 72 65 73 73 |HG20....Compress| 0010: 69 6f 6e 3d 47 5a 78 9c 63 60 60 d0 e4 76 f6 70 |ion=GZx.c``..v.p| 0020: f4 73 77 75 0f f2 0f 0d 60 00 02 46 46 76 26 4e |.swu....`..FFv&N| 0030: c6 b2 d4 a2 e2 cc fc 3c 03 a3 bc a4 e4 8c c4 bc |.......<........| - 0040: f4 d4 62 23 06 06 e6 65 40 f9 4d c1 2a 31 09 cf |..b#...e@.M.*1..| + 0040: f4 d4 62 23 06 06 e6 19 40 f9 4d c1 2a 31 09 cf |..b#....@.M.*1..| 0050: 9a 3a 52 04 b7 fc db f0 95 e5 a4 f4 97 17 b2 c9 |.:R.............| 0060: 0c 14 00 02 e6 d9 99 25 1a a7 a4 99 a4 a4 1a 5b |.......%.......[| 0070: 58 a4 19 27 9b a4 59 a4 1a 59 a4 99 a4 59 26 5a |X..'..Y..Y...Y&Z| 0080: 18 9a 18 59 5a 26 1a 27 27 25 99 a6 99 1a 70 95 |...YZ&.''%....p.| 0090: a4 16 97 70 19 28 18 70 a5 e5 e7 73 71 25 a6 a4 |...p.(.p...sq%..| - 00a0: 28 00 19 40 13 0e ac fa df ab ff 7b 3f fb 92 dc |(..@.......{?...| - 00b0: 8b 1f 62 bb 9e b7 d7 d9 87 3d 5a 44 ac 2f b0 a9 |..b......=ZD./..| - 00c0: c3 66 1e 54 b9 26 08 a7 1a 1b 1a a7 25 1b 9a 1b |.f.T.&......%...| - 00d0: 99 19 9a 5a 18 9b a6 18 19 00 dd 67 61 61 98 06 |...Z.......gaa..| - 00e0: f4 80 49 4a 8a 65 52 92 41 9a 81 81 a5 11 17 50 |..IJ.eR.A......P| - 00f0: 31 30 58 19 cc 80 98 25 29 b1 08 c4 37 07 79 19 |10X....%)...7.y.| - 0100: 88 d9 41 ee 07 8a 41 cd 5d 98 65 fb e5 9e 45 bf |..A...A.].e...E.| - 0110: 8d 7f 9f c6 97 9f 2b 44 34 67 d9 ec 8e 0f a0 61 |......+D4g.....a| - 0120: a8 eb 82 82 2e c9 c2 20 25 d5 34 c5 d0 d8 c2 dc |....... %.4.....| - 0130: d4 c2 d4 c4 30 d9 34 cd c0 d4 c8 cc 34 31 c5 d0 |....0.4.....41..| - 0140: c4 24 31 c9 32 2d d1 c2 2c c5 30 25 09 e4 ee 85 |.$1.2-..,.0%....| - 0150: 8f 85 ff 88 ab 89 36 c7 2a c4 47 34 fe f8 ec 7b |......6.*.G4...{| - 0160: 73 37 3f c3 24 62 1d 8d 4d 1d 9e 40 06 3b 10 14 |s7?.$b..M..@.;..| - 0170: 36 a4 38 10 04 d8 21 01 9a b1 83 f7 e9 45 8b d2 |6.8...!......E..| - 0180: 56 c7 a3 1f 82 52 d7 8a 78 ed fc d5 76 f1 36 25 |V....R..x...v.6%| - 0190: 81 89 c7 ad ec 90 34 48 75 2b 89 49 bf 00 cf 72 |......4Hu+.I...r| - 01a0: f4 7f |..| + 00a0: 28 00 19 20 17 af fa df ab ff 7b 3f fb 92 dc 8b |(.. ......{?....| + 00b0: 1f 62 bb 9e b7 d7 d9 87 3d 5a 44 89 2f b0 99 87 |.b......=ZD./...| + 00c0: ec e2 54 63 43 e3 b4 64 43 73 23 33 43 53 0b 63 |..TcC..dCs#3CS.c| + 00d0: d3 14 23 03 a0 fb 2c 2c 0c d3 80 1e 30 49 49 b1 |..#...,,....0II.| + 00e0: 4c 4a 32 48 33 30 b0 34 42 b8 38 29 b1 08 e2 62 |LJ2H30.4B.8)...b| + 00f0: 20 03 6a ca c2 2c db 2f f7 2c fa 6d fc fb 34 be | .j..,./.,.m..4.| + 0100: fc 5c 21 a2 39 cb 66 77 7c 00 0d c3 59 17 14 58 |.\!.9.fw|...Y..X| + 0110: 49 16 06 29 a9 a6 29 86 c6 16 e6 a6 16 a6 26 86 |I..)..).......&.| + 0120: c9 a6 69 06 a6 46 66 a6 89 29 86 26 26 89 49 96 |..i..Ff..).&&.I.| + 0130: 69 89 16 66 29 86 29 49 5c 20 07 3e 16 fe 23 ae |i..f).)I\ .>..#.| + 0140: 26 da 1c ab 10 1f d1 f8 e3 b3 ef cd dd fc 0c 93 |&...............| + 0150: 88 75 34 36 75 04 82 55 17 14 36 a4 38 10 04 d8 |.u46u..U..6.8...| + 0160: 21 01 9a b1 83 f7 e9 45 8b d2 56 c7 a3 1f 82 52 |!......E..V....R| + 0170: d7 8a 78 ed fc d5 76 f1 36 25 81 89 c7 ad ec 90 |..x...v.6%......| + 0180: 54 47 75 2b 89 49 b1 00 d2 8a eb 92 |TGu+.I......| $ echo "http://localhost:$HGPORT1/full.hg" > server/.hg/clonebundles.manifest $ hg clone -U http://localhost:$HGPORT full-bundle
--- a/tests/test-command-template.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-command-template.t Tue Oct 18 14:15:15 2016 -0500 @@ -29,6 +29,111 @@ $ hg merge -q foo $ hg commit -m 'merge' -d '1500001 0' -u 'person' +Test arithmetic operators have the right precedence: + + $ hg log -l 1 -T '{date(date, "%s") + 5 * 10} {date(date, "%s") - 2 * 3}\n' + 1500051 1499995 + $ hg log -l 1 -T '{date(date, "%s") * 5 + 10} {date(date, "%s") * 3 - 2}\n' + 7500015 4500001 + +Test division: + + $ hg debugtemplate -r0 -v '{5 / 2} {mod(5, 2)}\n' + (template + (/ + ('integer', '5') + ('integer', '2')) + ('string', ' ') + (func + ('symbol', 'mod') + (list + ('integer', '5') + ('integer', '2'))) + ('string', '\n')) + 2 1 + $ hg debugtemplate -r0 -v '{5 / -2} {mod(5, -2)}\n' + (template + (/ + ('integer', '5') + (negate + ('integer', '2'))) + ('string', ' ') + (func + ('symbol', 'mod') + (list + ('integer', '5') + (negate + ('integer', '2')))) + ('string', '\n')) + -3 -1 + $ hg debugtemplate -r0 -v '{-5 / 2} {mod(-5, 2)}\n' + (template + (/ + (negate + ('integer', '5')) + ('integer', '2')) + ('string', ' ') + (func + ('symbol', 'mod') + (list + (negate + ('integer', '5')) + ('integer', '2'))) + ('string', '\n')) + -3 1 + $ hg debugtemplate -r0 -v '{-5 / -2} {mod(-5, -2)}\n' + (template + (/ + (negate + ('integer', '5')) + (negate + ('integer', '2'))) + ('string', ' ') + (func + ('symbol', 'mod') + (list + (negate + ('integer', '5')) + (negate + ('integer', '2')))) + ('string', '\n')) + 2 -1 + +Filters bind closer than arithmetic: + + $ hg debugtemplate -r0 -v '{revset(".")|count - 1}\n' + (template + (- + (| + (func + ('symbol', 'revset') + ('string', '.')) + ('symbol', 'count')) + ('integer', '1')) + ('string', '\n')) + 0 + +But negate binds closer still: + + $ hg debugtemplate -r0 -v '{1-3|stringify}\n' + (template + (- + ('integer', '1') + (| + ('integer', '3') + ('symbol', 'stringify'))) + ('string', '\n')) + hg: parse error: arithmetic only defined on integers + [255] + $ hg debugtemplate -r0 -v '{-3|stringify}\n' + (template + (| + (negate + ('integer', '3')) + ('symbol', 'stringify')) + ('string', '\n')) + -3 + Second branch starting at nullrev: $ hg update null @@ -103,6 +208,18 @@ $ hg log -l1 -T./map-simple 8 +Test template map inheritance + + $ echo "__base__ = map-cmdline.default" > map-simple + $ printf 'cset = "changeset: ***{rev}***\\n"\n' >> map-simple + $ hg log -l1 -T./map-simple + changeset: ***8*** + tag: tip + user: test + date: Wed Jan 01 10:01:00 2020 +0000 + summary: third + + Template should precede style option $ hg log -l1 --style default -T '{rev}\n' @@ -2878,14 +2995,15 @@ $ hg debugtemplate -v '{(-4)}\n' (template (group - ('integer', '-4')) + (negate + ('integer', '4'))) ('string', '\n')) -4 $ hg debugtemplate '{(-)}\n' - hg: parse error at 2: integer literal without digits + hg: parse error at 3: not a prefix: ) [255] $ hg debugtemplate '{(-a)}\n' - hg: parse error at 2: integer literal without digits + hg: parse error: negation needs an integer argument [255] top-level integer literal is interpreted as symbol (i.e. variable name): @@ -3202,6 +3320,11 @@ hg: parse error: fill expects an integer width [255] + $ COLUMNS=25 hg log -l1 --template '{fill(desc, termwidth, "{node|short}:", "termwidth.{rev}:")}' + bcc7ff960b8e:desc to be + termwidth.1:wrapped desc + termwidth.1:to be wrapped (no-eol) + $ hg log -l 1 --template '{sub(r"[0-9]", "-", author)}' {node|short} (no-eol) $ hg log -l 1 --template '{sub(r"[0-9]", "-", "{node|short}")}' @@ -3323,6 +3446,27 @@ hg: parse error: pad() expects an integer width [255] +Test boolean argument passed to pad function + + no crash + + $ hg log -r 0 -T '{pad(rev, 10, "-", "f{"oo"}")}\n' + ---------0 + + string/literal + + $ hg log -r 0 -T '{pad(rev, 10, "-", "false")}\n' + ---------0 + $ hg log -r 0 -T '{pad(rev, 10, "-", false)}\n' + 0--------- + $ hg log -r 0 -T '{pad(rev, 10, "-", "")}\n' + 0--------- + + unknown keyword is evaluated to '' + + $ hg log -r 0 -T '{pad(rev, 10, "-", unknownkeyword)}\n' + 0--------- + Test separate function $ hg log -r 0 -T '{separate("-", "", "a", "b", "", "", "c", "")}\n' @@ -3332,6 +3476,23 @@ $ 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 boolean expression/literal passed to if function + + $ hg log -r 0 -T '{if(rev, "rev 0 is True")}\n' + rev 0 is True + $ hg log -r 0 -T '{if(0, "literal 0 is True as well")}\n' + literal 0 is True as well + $ hg log -r 0 -T '{if("", "", "empty string is False")}\n' + empty string is False + $ hg log -r 0 -T '{if(revset(r"0 - 0"), "", "empty list is False")}\n' + empty list is False + $ hg log -r 0 -T '{if(true, "true is True")}\n' + true is True + $ hg log -r 0 -T '{if(false, "", "false is False")}\n' + false is False + $ hg log -r 0 -T '{if("false", "non-empty string is True")}\n' + non-empty string is True + Test ifcontains function $ hg log --template '{rev} {ifcontains(rev, "2 two 0", "is in the string", "is not")}\n' @@ -3451,6 +3612,35 @@ 5:13207e5a10d9fd28ec424934298e176197f2c67f, 4:bbe44766e73d5f11ed2177f1838de10c53ef3e74 +Test files function + + $ hg log -T "{rev}\n{join(files('*'), '\n')}\n" + 2 + a + aa + b + 1 + a + 0 + a + + $ hg log -T "{rev}\n{join(files('aa'), '\n')}\n" + 2 + aa + 1 + + 0 + + +Test relpath function + + $ hg log -r0 -T '{files % "{file|relpath}\n"}' + a + $ cd .. + $ hg log -R r -r0 -T '{files % "{file|relpath}\n"}' + r/a (glob) + $ cd r + Test active bookmark templating $ hg book foo
--- a/tests/test-commandserver.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-commandserver.t Tue Oct 18 14:15:15 2016 -0500 @@ -78,7 +78,7 @@ summary summarize working directory state update update working directory (or switch revisions) - (use "hg help" for the full list of commands or "hg -v" for details) + (use 'hg help' for the full list of commands or 'hg -v' for details) *** runcommand id --quiet 000000000000 *** runcommand id
--- a/tests/test-commit-amend.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-commit-amend.t Tue Oct 18 14:15:15 2016 -0500 @@ -120,13 +120,13 @@ stripping amended changeset 74609c7f506e 1 changesets found uncompressed size of bundle content: - 270 (changelog) + 254 (changelog) 163 (manifests) 129 a saved backup bundle to $TESTTMP/.hg/strip-backup/74609c7f506e-1bfde511-amend-backup.hg (glob) 1 changesets found uncompressed size of bundle content: - 266 (changelog) + 250 (changelog) 163 (manifests) 129 a adding branch @@ -264,13 +264,13 @@ stripping amended changeset 5f357c7560ab 1 changesets found uncompressed size of bundle content: - 258 (changelog) + 249 (changelog) 163 (manifests) 131 a saved backup bundle to $TESTTMP/.hg/strip-backup/5f357c7560ab-e7c84ade-amend-backup.hg (glob) 1 changesets found uncompressed size of bundle content: - 266 (changelog) + 257 (changelog) 163 (manifests) 131 a adding branch @@ -307,13 +307,13 @@ stripping amended changeset 7ab3bf440b54 2 changesets found uncompressed size of bundle content: - 490 (changelog) + 464 (changelog) 322 (manifests) 249 a saved backup bundle to $TESTTMP/.hg/strip-backup/7ab3bf440b54-8e3b5088-amend-backup.hg (glob) 1 changesets found uncompressed size of bundle content: - 266 (changelog) + 257 (changelog) 163 (manifests) 133 a adding branch @@ -638,7 +638,7 @@ (no more unresolved files) $ hg ci -m 'merge bar' $ hg log --config diff.git=1 -pr . - changeset: 23:93cd4445f720 + changeset: 23:69c24fe01e35 tag: tip parent: 22:30d96aeaf27b parent: 21:1aa437659d19 @@ -653,11 +653,11 @@ --- a/cc +++ b/cc @@ -1,1 +1,5 @@ - +<<<<<<< local: 30d96aeaf27b - test: aa + +<<<<<<< working copy: 30d96aeaf27b - test: aa dd +======= +cc - +>>>>>>> other: 1aa437659d19 bar - test: aazzcc + +>>>>>>> merge rev: 1aa437659d19 bar - test: aazzcc diff --git a/z b/zz rename from z rename to zz @@ -671,7 +671,7 @@ $ HGEDITOR="sh .hg/checkeditform.sh" hg ci --amend -m 'merge bar (amend message)' --edit HGEDITFORM=commit.amend.merge $ hg log --config diff.git=1 -pr . - changeset: 24:832b50f2c271 + changeset: 24:cfa2fbef3169 tag: tip parent: 22:30d96aeaf27b parent: 21:1aa437659d19 @@ -686,11 +686,11 @@ --- a/cc +++ b/cc @@ -1,1 +1,5 @@ - +<<<<<<< local: 30d96aeaf27b - test: aa + +<<<<<<< working copy: 30d96aeaf27b - test: aa dd +======= +cc - +>>>>>>> other: 1aa437659d19 bar - test: aazzcc + +>>>>>>> merge rev: 1aa437659d19 bar - test: aazzcc diff --git a/z b/zz rename from z rename to zz @@ -704,7 +704,7 @@ $ hg mv zz z $ hg ci --amend -m 'merge bar (undo rename)' $ hg log --config diff.git=1 -pr . - changeset: 26:bdafc5c72f74 + changeset: 26:c34de68b014c tag: tip parent: 22:30d96aeaf27b parent: 21:1aa437659d19 @@ -719,11 +719,11 @@ --- a/cc +++ b/cc @@ -1,1 +1,5 @@ - +<<<<<<< local: 30d96aeaf27b - test: aa + +<<<<<<< working copy: 30d96aeaf27b - test: aa dd +======= +cc - +>>>>>>> other: 1aa437659d19 bar - test: aazzcc + +>>>>>>> merge rev: 1aa437659d19 bar - test: aazzcc $ hg debugrename z z not renamed @@ -740,9 +740,9 @@ $ echo aa >> aaa $ hg ci -m 'merge bar again' $ hg log --config diff.git=1 -pr . - changeset: 28:32f19415b634 + changeset: 28:37d40dcef03b tag: tip - parent: 26:bdafc5c72f74 + parent: 26:c34de68b014c parent: 27:4c94d5bc65f5 user: test date: Thu Jan 01 00:00:00 1970 +0000 @@ -775,9 +775,9 @@ $ hg mv aaa aa $ hg ci --amend -m 'merge bar again (undo rename)' $ hg log --config diff.git=1 -pr . - changeset: 30:1e2a06b3d312 + changeset: 30:537c6d1b3633 tag: tip - parent: 26:bdafc5c72f74 + parent: 26:c34de68b014c parent: 27:4c94d5bc65f5 user: test date: Thu Jan 01 00:00:00 1970 +0000 @@ -813,13 +813,13 @@ $ hg merge -q bar --config ui.interactive=True << EOF > c > EOF - local changed aa which remote deleted + local [working copy] changed aa which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? c $ hg ci -m 'merge bar (with conflicts)' $ hg log --config diff.git=1 -pr . - changeset: 33:97a298b0c59f + changeset: 33:7afcba911942 tag: tip - parent: 32:3d78ce4226b8 + parent: 32:6075d69d215d parent: 31:67db8847a540 user: test date: Thu Jan 01 00:00:00 1970 +0000 @@ -829,9 +829,9 @@ $ hg rm aa $ hg ci --amend -m 'merge bar (with conflicts, amended)' $ hg log --config diff.git=1 -pr . - changeset: 35:6de0c1bde1c8 + changeset: 35:376965e47ddd tag: tip - parent: 32:3d78ce4226b8 + parent: 32:6075d69d215d parent: 31:67db8847a540 user: test date: Thu Jan 01 00:00:00 1970 +0000 @@ -927,7 +927,7 @@ HG: M: HG: A: foo HG: R: - HG: diff -r 6de0c1bde1c8 foo + HG: diff -r 376965e47ddd foo HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/foo Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@ @@ -941,12 +941,12 @@ HG: M: HG: A: foo y HG: R: - HG: diff -r 6de0c1bde1c8 foo + HG: diff -r 376965e47ddd foo HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/foo Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@ HG: +foo - HG: diff -r 6de0c1bde1c8 y + HG: diff -r 376965e47ddd y HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/y Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@ @@ -959,18 +959,18 @@ HG: M: HG: A: foo y HG: R: a - HG: diff -r 6de0c1bde1c8 a + HG: diff -r 376965e47ddd a HG: --- a/a Thu Jan 01 00:00:00 1970 +0000 HG: +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: @@ -1,2 +0,0 @@ HG: -a HG: -a - HG: diff -r 6de0c1bde1c8 foo + HG: diff -r 376965e47ddd foo HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/foo Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@ HG: +foo - HG: diff -r 6de0c1bde1c8 y + HG: diff -r 376965e47ddd y HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/y Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@ @@ -983,23 +983,23 @@ HG: M: HG: A: foo y HG: R: a x - HG: diff -r 6de0c1bde1c8 a + HG: diff -r 376965e47ddd a HG: --- a/a Thu Jan 01 00:00:00 1970 +0000 HG: +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: @@ -1,2 +0,0 @@ HG: -a HG: -a - HG: diff -r 6de0c1bde1c8 foo + HG: diff -r 376965e47ddd foo HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/foo Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@ HG: +foo - HG: diff -r 6de0c1bde1c8 x + HG: diff -r 376965e47ddd x HG: --- a/x Thu Jan 01 00:00:00 1970 +0000 HG: +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: @@ -1,1 +0,0 @@ HG: -x - HG: diff -r 6de0c1bde1c8 y + HG: diff -r 376965e47ddd y HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/y Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@ @@ -1014,23 +1014,23 @@ HG: M: HG: A: foo y HG: R: a x - HG: diff -r 6de0c1bde1c8 a + HG: diff -r 376965e47ddd a HG: --- a/a Thu Jan 01 00:00:00 1970 +0000 HG: +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: @@ -1,2 +0,0 @@ HG: -a HG: -a - HG: diff -r 6de0c1bde1c8 foo + HG: diff -r 376965e47ddd foo HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/foo Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@ HG: +foo - HG: diff -r 6de0c1bde1c8 x + HG: diff -r 376965e47ddd x HG: --- a/x Thu Jan 01 00:00:00 1970 +0000 HG: +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: @@ -1,1 +0,0 @@ HG: -x - HG: diff -r 6de0c1bde1c8 y + HG: diff -r 376965e47ddd y HG: --- /dev/null Thu Jan 01 00:00:00 1970 +0000 HG: +++ b/y Thu Jan 01 00:00:00 1970 +0000 HG: @@ -0,0 +1,1 @@
--- a/tests/test-commit-interactive-curses.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-commit-interactive-curses.t Tue Oct 18 14:15:15 2016 -0500 @@ -37,6 +37,7 @@ > EOF $ hg commit -i -m "a" -d "0 0" no changes to record + [1] $ hg tip changeset: -1:000000000000 tag: tip @@ -60,6 +61,7 @@ Check that commit -i works with no changes $ hg commit -i no changes to record + [1] Committing only one file
--- a/tests/test-commit-interactive.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-commit-interactive.t Tue Oct 18 14:15:15 2016 -0500 @@ -29,6 +29,7 @@ examine changes to 'empty-rw'? [Ynesfdaq?] n no changes to record + [1] $ hg tip -p changeset: -1:000000000000 @@ -1376,6 +1377,7 @@ record this change to 'editedfile'? [Ynesfdaq?] e no changes to record + [1] $ cat editedfile This change will not be committed This is the second line @@ -1487,6 +1489,7 @@ record this change to 'editedfile'? [Ynesfdaq?] n no changes to record + [1] random text in random positions is still an error
--- a/tests/test-commit-unresolved.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-commit-unresolved.t Tue Oct 18 14:15:15 2016 -0500 @@ -34,7 +34,7 @@ $ echo "ABCD" > A $ hg commit -m "Merged" - abort: unresolved merge conflicts (see "hg help resolve") + abort: unresolved merge conflicts (see 'hg help resolve') [255] Mark the conflict as resolved and commit @@ -56,7 +56,7 @@ [1] $ hg rm --force A $ hg commit -m merged - abort: unresolved merge conflicts (see "hg help resolve") + abort: unresolved merge conflicts (see 'hg help resolve') [255] $ hg resolve -ma
--- a/tests/test-completion.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-completion.t Tue Oct 18 14:15:15 2016 -0500 @@ -232,7 +232,7 @@ branches: active, closed, template bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure cat: output, rev, decode, include, exclude - config: untrusted, edit, local, global + config: untrusted, edit, local, global, template copy: after, force, include, exclude, dry-run debugancestor: debugapplystreamclonebundle: @@ -261,7 +261,7 @@ debuglocks: force-lock, force-wlock debugmergestate: debugnamecomplete: - debugobsolete: flags, record-parents, rev, index, delete, date, user + debugobsolete: flags, record-parents, rev, index, delete, date, user, template debugpathcomplete: full, normal, added, removed debugpushkey: debugpvec: @@ -269,7 +269,7 @@ debugrebuildfncache: debugrename: rev debugrevlog: changelog, manifest, dir, dump - debugrevspec: optimize + debugrevspec: optimize, show-stage, no-optimized, verify-optimized debugsetparents: debugsub: rev debugsuccessorssets: @@ -278,7 +278,7 @@ debugwireargs: three, four, five, ssh, remotecmd, insecure files: rev, print0, include, exclude, template, subrepos graft: rev, continue, edit, log, force, currentdate, currentuser, date, user, tool, dry-run - grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude + grep: print0, all, text, follow, ignore-case, files-with-matches, line-number, rev, user, date, template, include, exclude heads: rev, topo, active, closed, style, template help: extension, command, keyword, system identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure @@ -301,7 +301,7 @@ tip: patch, git, style, template unbundle: update verify: - version: + version: template $ hg init a $ cd a
--- a/tests/test-config.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-config.t Tue Oct 18 14:15:15 2016 -0500 @@ -54,6 +54,36 @@ Section.KeY=Case Sensitive Section.key=lower case + $ hg showconfig Section -Tjson + [ + { + "name": "Section.KeY", + "source": "*.hgrc:16", (glob) + "value": "Case Sensitive" + }, + { + "name": "Section.key", + "source": "*.hgrc:17", (glob) + "value": "lower case" + } + ] + $ hg showconfig Section.KeY -Tjson + [ + { + "name": "Section.KeY", + "source": "*.hgrc:16", (glob) + "value": "Case Sensitive" + } + ] + $ hg showconfig -Tjson | tail -7 + }, + { + "name": "*", (glob) + "source": "*", (glob) + "value": "*" (glob) + } + ] + Test "%unset" $ cat >> $HGRCPATH <<EOF
--- a/tests/test-conflict.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-conflict.t Tue Oct 18 14:15:15 2016 -0500 @@ -49,13 +49,13 @@ 1 2 3 - <<<<<<< local: 618808747361 - test: branch2 + <<<<<<< working copy: 618808747361 - test: branch2 6 8 ======= 4 5 - >>>>>>> other: c0c68e4fe667 - test: branch1 + >>>>>>> merge rev: c0c68e4fe667 - test: branch1 Hop we are done. $ hg status @@ -79,13 +79,13 @@ 1 2 3 - <<<<<<< local: test 2 + <<<<<<< working copy: test 2 6 8 ======= 4 5 - >>>>>>> other: test 1 + >>>>>>> merge rev: test 1 Hop we are done. Verify line splitting of custom conflict marker which causes multiple lines @@ -105,13 +105,13 @@ 1 2 3 - <<<<<<< local: test 2 + <<<<<<< working copy: test 2 6 8 ======= 4 5 - >>>>>>> other: test 1 + >>>>>>> merge rev: test 1 Hop we are done. Verify line trimming of custom conflict marker using multi-byte characters @@ -144,13 +144,13 @@ 1 2 3 - <<<<<<< local: 123456789012345678901234567890123456789012345678901234567890\xe3\x81\x82... (esc) + <<<<<<< working copy: 1234567890123456789012345678901234567890123456789012345... 6 8 ======= 4 5 - >>>>>>> other: branch1 + >>>>>>> merge rev: branch1 Hop we are done. Verify basic conflict markers @@ -170,13 +170,13 @@ 1 2 3 - <<<<<<< local + <<<<<<< working copy 6 8 ======= 4 5 - >>>>>>> other + >>>>>>> merge rev Hop we are done. internal:merge3 @@ -191,7 +191,7 @@ [1] $ cat a Small Mathematical Series. - <<<<<<< local + <<<<<<< working copy 1 2 3 @@ -209,7 +209,7 @@ 3 4 5 - >>>>>>> other + >>>>>>> merge rev Hop we are done. Add some unconflicting changes on each head, to make sure we really
--- a/tests/test-contrib-perf.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-contrib-perf.t Tue Oct 18 14:15:15 2016 -0500 @@ -53,6 +53,8 @@ perfbranchmap benchmark the update of a branchmap perfcca (no help text available) + perfchangegroupchangelog + Benchmark producing a changelog group for a changegroup. perfchangeset (no help text available) perfctxfiles (no help text available) @@ -105,13 +107,14 @@ benchmark the computation of various volatile set perfwalk (no help text available) - (use "hg help -v perfstatusext" to show built-in aliases and global options) + (use 'hg help -v perfstatusext' to show built-in aliases and global options) $ hg perfaddremove $ hg perfancestors $ hg perfancestorset 2 $ hg perfannotate a $ hg perfbranchmap $ hg perfcca + $ hg perfchangegroupchangelog $ hg perfchangeset 2 $ hg perfctxfiles 2 $ hg perfdiffwd
--- a/tests/test-copy-move-merge.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-copy-move-merge.t Tue Oct 18 14:15:15 2016 -0500 @@ -85,7 +85,7 @@ > c > EOF rebasing 2:add3f11052fa "other" (tip) - remote changed a which local deleted + other [source] changed a which local [dest] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c $ cat b
--- a/tests/test-copy.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-copy.t Tue Oct 18 14:15:15 2016 -0500 @@ -224,10 +224,25 @@ foo was clean: $ hg st -AC foo C foo +Trying to copy on top of an existing file fails, + $ hg copy -A bar foo + foo: not overwriting - file already committed + (hg copy --after --force to replace the file by recording a copy) +same error without the --after, so the user doesn't have to go through +two hints: + $ hg copy bar foo + foo: not overwriting - file already committed + (hg copy --force to replace the file by recording a copy) but it's considered modified after a copy --after --force $ hg copy -Af bar foo $ hg st -AC foo M foo bar +The hint for a file that exists but is not in file history doesn't +mention --force: + $ touch xyzzy + $ hg cp bar xyzzy + xyzzy: not overwriting - file exists + (hg copy --after to record the copy) $ cd ..
--- a/tests/test-debugbundle.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-debugbundle.t Tue Oct 18 14:15:15 2016 -0500 @@ -16,7 +16,7 @@ $ hg bundle --base 0 --rev tip bundle2.hg -v --type none-v2 2 changesets found uncompressed size of bundle content: - 372 (changelog) + 344 (changelog) 322 (manifests) 113 b 113 c @@ -60,8 +60,8 @@ format: id, p1, p2, cset, delta base, len(delta) changelog - 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 80 - 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 80 + 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 66 + 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0000000000000000000000000000000000000000 66 manifest 686dbf0aeca417636fa26a9121c681eabbb15a20 8515d4bfda768e04af4c13a69a72e28c7effbea7 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 8515d4bfda768e04af4c13a69a72e28c7effbea7 55
--- a/tests/test-debugextensions.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-debugextensions.t Tue Oct 18 14:15:15 2016 -0500 @@ -4,6 +4,10 @@ $ cat > extwithoutinfos.py <<EOF > EOF + $ cat > extwithinfos.py <<EOF + > testedwith = '3.0 3.1 3.2.1' + > buglink = 'https://example.org/bts' + > EOF $ cat >> $HGRCPATH <<EOF > [extensions] @@ -13,11 +17,13 @@ > rebase= > mq= > ext1 = $debugpath + > ext2 = `pwd`/extwithinfos.py > EOF $ hg debugextensions color ext1 (untested!) + ext2 (3.2.1!) histedit mq patchbomb @@ -26,58 +32,82 @@ $ hg debugextensions -v color location: */hgext/color.py* (glob) - tested with: internal + bundled: yes ext1 location: */extwithoutinfos.py* (glob) + bundled: no + ext2 + location: */extwithinfos.py* (glob) + bundled: no + tested with: 3.0 3.1 3.2.1 + bug reporting: https://example.org/bts histedit location: */hgext/histedit.py* (glob) - tested with: internal + bundled: yes mq location: */hgext/mq.py* (glob) - tested with: internal + bundled: yes patchbomb location: */hgext/patchbomb.py* (glob) - tested with: internal + bundled: yes rebase location: */hgext/rebase.py* (glob) - tested with: internal + bundled: yes $ hg debugextensions -Tjson | sed 's|\\\\|/|g' [ { "buglink": "", + "bundled": true, "name": "color", "source": "*/hgext/color.py*", (glob) - "testedwith": "internal" + "testedwith": [] }, { "buglink": "", + "bundled": false, "name": "ext1", "source": "*/extwithoutinfos.py*", (glob) - "testedwith": "" + "testedwith": [] }, { - "buglink": "", - "name": "histedit", - "source": "*/hgext/histedit.py*", (glob) - "testedwith": "internal" + "buglink": "https://example.org/bts", + "bundled": false, + "name": "ext2", + "source": "*/extwithinfos.py*", (glob) + "testedwith": ["3.0", "3.1", "3.2.1"] }, { "buglink": "", + "bundled": true, + "name": "histedit", + "source": "*/hgext/histedit.py*", (glob) + "testedwith": [] + }, + { + "buglink": "", + "bundled": true, "name": "mq", "source": "*/hgext/mq.py*", (glob) - "testedwith": "internal" + "testedwith": [] }, { "buglink": "", + "bundled": true, "name": "patchbomb", "source": "*/hgext/patchbomb.py*", (glob) - "testedwith": "internal" + "testedwith": [] }, { "buglink": "", + "bundled": true, "name": "rebase", "source": "*/hgext/rebase.py*", (glob) - "testedwith": "internal" + "testedwith": [] } ] + + $ hg debugextensions -T '{ifcontains("3.1", testedwith, "{name}\n")}' + ext2 + $ hg debugextensions \ + > -T '{ifcontains("3.2", testedwith, "no substring match: {name}\n")}'
--- a/tests/test-default-push.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-default-push.t Tue Oct 18 14:15:15 2016 -0500 @@ -19,7 +19,7 @@ $ cd c $ hg push --config paths.default= abort: default repository not configured! - (see the "path" section in "hg help config") + (see 'hg help config.paths') [255] $ cd ..
--- a/tests/test-demandimport.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-demandimport.py Tue Oct 18 14:15:15 2016 -0500 @@ -4,6 +4,14 @@ demandimport.enable() import os +import subprocess +import sys + +# Only run if demandimport is allowed +if subprocess.call(['python', '%s/hghave' % os.environ['TESTDIR'], + 'demandimport']): + sys.exit(80) + if os.name != 'nt': try: import distutils.msvc9compiler @@ -55,8 +63,18 @@ print("re.stderr =", f(re.stderr)) print("re =", f(re)) +import contextlib +print("contextlib =", f(contextlib)) +try: + from contextlib import unknownattr + print('no demandmod should be created for attribute of non-package ' + 'module:\ncontextlib.unknownattr =', f(unknownattr)) +except ImportError as inst: + print('contextlib.unknownattr = ImportError: %s' % inst) + demandimport.disable() os.environ['HGDEMANDIMPORT'] = 'disable' +# this enable call should not actually enable demandimport! demandimport.enable() from mercurial import node print("node =", f(node))
--- a/tests/test-demandimport.py.out Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-demandimport.py.out Tue Oct 18 14:15:15 2016 -0500 @@ -16,4 +16,6 @@ re = <unloaded module 'sys'> re.stderr = <open file '<whatever>', mode 'w' at 0x?> re = <proxied module 'sys'> +contextlib = <unloaded module 'contextlib'> +contextlib.unknownattr = ImportError: cannot import name unknownattr node = <module 'mercurial.node' from '?'>
--- a/tests/test-devel-warnings.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-devel-warnings.t Tue Oct 18 14:15:15 2016 -0500 @@ -91,10 +91,11 @@ */mercurial/dispatch.py:* in run (glob) */mercurial/dispatch.py:* in dispatch (glob) */mercurial/dispatch.py:* in _runcatch (glob) + */mercurial/dispatch.py:* in callcatch (glob) + */mercurial/dispatch.py:* in _runcatchfunc (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) @@ -125,10 +126,11 @@ */mercurial/dispatch.py:* in run (glob) */mercurial/dispatch.py:* in dispatch (glob) */mercurial/dispatch.py:* in _runcatch (glob) + */mercurial/dispatch.py:* in callcatch (glob) + */mercurial/dispatch.py:* in _runcatchfunc (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 oldanddeprecated (glob) @@ -147,10 +149,11 @@ */mercurial/dispatch.py:* in run (glob) */mercurial/dispatch.py:* in dispatch (glob) */mercurial/dispatch.py:* in _runcatch (glob) + */mercurial/dispatch.py:* in callcatch (glob) + */mercurial/dispatch.py:* in _runcatchfunc (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 oldanddeprecated (glob)
--- a/tests/test-diff-change.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-diff-change.t Tue Oct 18 14:15:15 2016 -0500 @@ -50,6 +50,13 @@ @@ -1,1 +1,1 @@ -third +wdir + $ hg diff -r '(2:2)' --nodates + diff -r bf5ff72eb7e0 file.txt + --- a/file.txt + +++ b/file.txt + @@ -1,1 +1,1 @@ + -third + +wdir $ hg diff -r 2::2 --nodates diff -r bf5ff72eb7e0 file.txt --- a/file.txt
--- a/tests/test-diff-unified.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-diff-unified.t Tue Oct 18 14:15:15 2016 -0500 @@ -333,4 +333,20 @@ + return a + b + c + e; } +If [diff] git is set to true, but the user says --no-git, we should +*not* get git diffs + $ hg diff --nodates --config diff.git=1 --no-git + diff -r f2c7c817fa55 f1 + --- a/f1 + +++ b/f1 + @@ -2,6 +2,6 @@ + int a = 0; + int b = 1; + int c = 2; + - int d = 3; + - return a + b + c + d; + + int e = 3; + + return a + b + c + e; + } + $ cd ..
--- a/tests/test-dispatch.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-dispatch.t Tue Oct 18 14:15:15 2016 -0500 @@ -27,7 +27,7 @@ -I --include PATTERN [+] include names matching the given patterns -X --exclude PATTERN [+] exclude names matching the given patterns - (use "hg cat -h" to show more help) + (use 'hg cat -h' to show more help) [255] [defaults]
--- a/tests/test-excessive-merge.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-excessive-merge.t Tue Oct 18 14:15:15 2016 -0500 @@ -69,7 +69,7 @@ 1 60 62 ..... 1 96155394af80 5e0375449e74 000000000000 (re) 2 122 62 ..... 2 92cc4c306b19 5e0375449e74 000000000000 (re) 3 184 69 ..... 3 e16a66a37edd 92cc4c306b19 96155394af80 (re) - 4 253 29 ..... 4 2ee31f665a86 96155394af80 92cc4c306b19 (re) + 4 253 69 ..... 4 2ee31f665a86 96155394af80 92cc4c306b19 (re) revision 1 $ hg manifest --debug 1
--- a/tests/test-extdiff.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-extdiff.t Tue Oct 18 14:15:15 2016 -0500 @@ -31,10 +31,12 @@ $ hg help falabala hg falabala [OPTION]... [FILE]... - use 'echo' to diff repository (or selected files) + use external program to diff repository (or selected files) Show differences between revisions for the specified files, using the - 'echo' program. + following program: + + 'echo' When two revision arguments are given, then changes are shown between those revisions. If only one revision is specified then that revision is @@ -397,15 +399,16 @@ $ HGPLAIN=1 hg --config hgext.extdiff= --config extdiff.cmd.td=hi help -k xyzzy abort: no matches - (try "hg help" for a list of topics) + (try 'hg help' for a list of topics) [255] $ HGPLAIN=1 hg --config hgext.extdiff= --config extdiff.cmd.td=hi help td > /dev/null $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help -k xyzzy abort: no matches - (try "hg help" for a list of topics) + (try 'hg help' for a list of topics) [255] - $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help td | grep "^use" - use '\xa5\xa5' to diff repository (or selected files) + $ LC_MESSAGES=ja_JP.UTF-8 hg --config hgext.extdiff= --config extdiff.cmd.td=$U help td \ + > | grep "^ '" + '\xa5\xa5'
--- a/tests/test-extension.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-extension.t Tue Oct 18 14:15:15 2016 -0500 @@ -249,7 +249,7 @@ $TESTTMP/a (glob) #endif -#if absimport +#if demandimport absimport Examine whether module loading is delayed until actual refering, even though module is imported with "absolute_import" feature. @@ -432,6 +432,36 @@ REL: this is absextroot.xsub1.xsub2.called.func() REL: this relimporter imports 'this is absextroot.relimportee' +Examine whether sub-module is imported relatively as expected. + +See also issue5208 for detail about example case on Python 3.x. + + $ f -q $TESTTMP/extlibroot/lsub1/lsub2/notexist.py + $TESTTMP/extlibroot/lsub1/lsub2/notexist.py: file not found + + $ cat > $TESTTMP/notexist.py <<EOF + > text = 'notexist.py at root is loaded unintentionally\n' + > EOF + + $ cat > $TESTTMP/checkrelativity.py <<EOF + > from mercurial import cmdutil + > cmdtable = {} + > command = cmdutil.command(cmdtable) + > + > # demand import avoids failure of importing notexist here + > import extlibroot.lsub1.lsub2.notexist + > + > @command('checkrelativity', [], norepo=True) + > def checkrelativity(ui, *args, **opts): + > try: + > ui.write(extlibroot.lsub1.lsub2.notexist.text) + > return 1 # unintentional success + > except ImportError: + > pass # intentional failure + > EOF + + $ (PYTHONPATH=${PYTHONPATH}${PATHSEP}${TESTTMP}; hg --config extensions.checkrelativity=$TESTTMP/checkrelativity.py checkrelativity) + #endif $ cd .. @@ -562,7 +592,7 @@ graphlog command to view revision graphs from a shell (DEPRECATED) - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) Extension module help vs command help: @@ -586,7 +616,7 @@ compared to the working directory, and, when no revisions are specified, the working directory files are compared to its parent. - (use "hg help -e extdiff" to show help for the extdiff extension) + (use 'hg help -e extdiff' to show help for the extdiff extension) options ([+] can be repeated): @@ -666,7 +696,7 @@ extdiff use external program to diff repository (or selected files) - (use "hg help -v -e extdiff" to show built-in aliases and global options) + (use 'hg help -v -e extdiff' to show built-in aliases and global options) @@ -718,7 +748,7 @@ A range acts as a closed interval. This means that a range of 3:5 gives 3, 4 and 5. Similarly, a range of 9:6 gives 9, 8, 7, and 6. - use "hg help -c multirevs" to see help for the multirevs command + use 'hg help -c multirevs' to see help for the multirevs command @@ -740,7 +770,7 @@ multirevs command - (use "hg multirevs -h" to show more help) + (use 'hg multirevs -h' to show more help) [255] @@ -800,7 +830,7 @@ dodo Does nothing foofoo Writes 'Foo foo' - (use "hg help -v -e dodo" to show built-in aliases and global options) + (use 'hg help -v -e dodo' to show built-in aliases and global options) Make sure that '-v -e' prints list of built-in aliases along with extension help itself @@ -841,7 +871,7 @@ Does nothing - (use "hg help -e dodo" to show help for the dodo extension) + (use 'hg help -e dodo' to show help for the dodo extension) options: @@ -903,7 +933,7 @@ beep Writes 'Beep beep' something Does something - (use "hg help -v dudu" to show built-in aliases and global options) + (use 'hg help -v dudu' to show built-in aliases and global options) In case when extension name doesn't match any of its commands, help options '-v' and '-v -e' should be equivalent @@ -981,7 +1011,7 @@ patchbomb command to send changesets as (a series of) patch emails - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) $ hg qdel @@ -990,7 +1020,7 @@ mq manage a stack of patches - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) [255] @@ -1000,7 +1030,7 @@ churn command to display statistics about repository history - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) [255] @@ -1010,12 +1040,12 @@ $ hg help churn churn extension - command to display statistics about repository history - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) $ hg help patchbomb patchbomb extension - command to send changesets as (a series of) patch emails - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) Broken disabled extension and command: @@ -1035,7 +1065,7 @@ $ hg --config extensions.path=./path.py help broken broken extension - (no help text available) - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) $ cat > hgext/forest.py <<EOF @@ -1044,7 +1074,7 @@ $ hg --config extensions.path=./path.py help foo > /dev/null warning: error finding commands in $TESTTMP/hgext/forest.py (glob) abort: no such help topic: foo - (try "hg help --keyword foo") + (try 'hg help --keyword foo') [255] $ cat > throw.py <<EOF @@ -1194,7 +1224,7 @@ throw external 1.2.3 $ echo 'getversion = lambda: "1.twentythree"' >> throw.py $ rm -f throw.pyc throw.pyo - $ hg version -v --config extensions.throw=throw.py + $ hg version -v --config extensions.throw=throw.py --config extensions.strip= Mercurial Distributed SCM (version *) (glob) (see https://mercurial-scm.org for more information) @@ -1205,6 +1235,43 @@ Enabled extensions: throw external 1.twentythree + strip internal + + $ hg version -q --config extensions.throw=throw.py + Mercurial Distributed SCM (version *) (glob) + +Test JSON output of version: + + $ hg version -Tjson + [ + { + "extensions": [], + "ver": "*" (glob) + } + ] + + $ hg version --config extensions.throw=throw.py -Tjson + [ + { + "extensions": [{"bundled": false, "name": "throw", "ver": "1.twentythree"}], + "ver": "3.2.2" + } + ] + + $ hg version --config extensions.strip= -Tjson + [ + { + "extensions": [{"bundled": true, "name": "strip", "ver": null}], + "ver": "*" (glob) + } + ] + +Test template output of version: + + $ hg version --config extensions.throw=throw.py --config extensions.strip= \ + > -T'{extensions % "{name} {pad(ver, 16)} ({if(bundled, "internal", "external")})\n"}' + throw 1.twentythree (external) + strip (internal) Refuse to load extensions with minimum version requirements
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-extensions-wrapfunction.py Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,39 @@ +from __future__ import absolute_import, print_function + +from mercurial import extensions + +def genwrapper(x): + def f(orig, *args, **kwds): + return [x] + orig(*args, **kwds) + f.x = x + return f + +def getid(wrapper): + return getattr(wrapper, 'x', '-') + +wrappers = [genwrapper(i) for i in range(5)] + +class dummyclass(object): + def getstack(self): + return ['orig'] + +dummy = dummyclass() + +def batchwrap(wrappers): + for w in wrappers: + extensions.wrapfunction(dummy, 'getstack', w) + print('wrap %d: %s' % (getid(w), dummy.getstack())) + +def batchunwrap(wrappers): + for w in wrappers: + result = None + try: + result = extensions.unwrapfunction(dummy, 'getstack', w) + msg = str(dummy.getstack()) + except (ValueError, IndexError) as e: + msg = e.__class__.__name__ + print('unwrap %s: %s: %s' % (getid(w), getid(result), msg)) + +batchwrap(wrappers + [wrappers[0]]) +batchunwrap([(wrappers[i] if i >= 0 else None) + for i in [3, None, 0, 4, 0, 2, 1, None]])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test-extensions-wrapfunction.py.out Tue Oct 18 14:15:15 2016 -0500 @@ -0,0 +1,14 @@ +wrap 0: [0, 'orig'] +wrap 1: [1, 0, 'orig'] +wrap 2: [2, 1, 0, 'orig'] +wrap 3: [3, 2, 1, 0, 'orig'] +wrap 4: [4, 3, 2, 1, 0, 'orig'] +wrap 0: [0, 4, 3, 2, 1, 0, 'orig'] +unwrap 3: 3: [0, 4, 2, 1, 0, 'orig'] +unwrap -: 0: [4, 2, 1, 0, 'orig'] +unwrap 0: 0: [4, 2, 1, 'orig'] +unwrap 4: 4: [2, 1, 'orig'] +unwrap 0: -: ValueError +unwrap 2: 2: [1, 'orig'] +unwrap 1: 1: ['orig'] +unwrap -: -: IndexError
--- a/tests/test-filecache.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-filecache.py Tue Oct 18 14:15:15 2016 -0500 @@ -179,6 +179,56 @@ print("* file y created") print(repo.cached) +def antiambiguity(): + filename = 'ambigcheck' + + # try some times, because reproduction of ambiguity depends on + # "filesystem time" + for i in xrange(5): + fp = open(filename, 'w') + fp.write('FOO') + fp.close() + + oldstat = os.stat(filename) + if oldstat.st_ctime != oldstat.st_mtime: + # subsequent changing never causes ambiguity + continue + + repetition = 3 + + # repeat changing via checkambigatclosing, to examine whether + # st_mtime is advanced multiple times as expecetd + for i in xrange(repetition): + # explicit closing + fp = scmutil.checkambigatclosing(open(filename, 'a')) + fp.write('FOO') + fp.close() + + # implicit closing by "with" statement + with scmutil.checkambigatclosing(open(filename, 'a')) as fp: + fp.write('BAR') + + newstat = os.stat(filename) + if oldstat.st_ctime != newstat.st_ctime: + # timestamp ambiguity was naturally avoided while repetition + continue + + # st_mtime should be advanced "repetition * 2" times, because + # all changes occured at same time (in sec) + expected = (oldstat.st_mtime + repetition * 2) & 0x7fffffff + if newstat.st_mtime != expected: + print("'newstat.st_mtime %s is not %s (as %s + %s * 2)" % + (newstat.st_mtime, expected, oldstat.st_mtime, repetition)) + + # no more examination is needed regardless of result + 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 + print('basic:') print() basic(fakerepo()) @@ -191,3 +241,7 @@ print('setbeforeget:') print() setbeforeget(fakerepo()) +print() +print('antiambiguity:') +print() +antiambiguity()
--- a/tests/test-filecache.py.out Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-filecache.py.out Tue Oct 18 14:15:15 2016 -0500 @@ -58,3 +58,6 @@ * file y created creating string from function + +antiambiguity: +
--- a/tests/test-getbundle.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-getbundle.t Tue Oct 18 14:15:15 2016 -0500 @@ -170,7 +170,7 @@ $ hg debuggetbundle repo bundle -t bundle2 $ hg debugbundle bundle Stream params: {} - changegroup -- "sortdict([('version', '01'), ('nbchanges', '18')])" + changegroup -- "sortdict([('version', '01')])" 7704483d56b2a7b5db54dcee7c62378ac629b348 29a4d1f17bd3f0779ca0525bebb1cfb51067c738 713346a995c363120712aed1aee7e04afd867638
--- a/tests/test-globalopts.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-globalopts.t Tue Oct 18 14:15:15 2016 -0500 @@ -361,7 +361,7 @@ templating Template Usage urls URL Paths - (use "hg help -v" to show built-in aliases and global options) + (use 'hg help -v' to show built-in aliases and global options) @@ -444,7 +444,7 @@ templating Template Usage urls URL Paths - (use "hg help -v" to show built-in aliases and global options) + (use 'hg help -v' to show built-in aliases and global options) Not tested: --debugger
--- a/tests/test-glog-topological.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-glog-topological.t Tue Oct 18 14:15:15 2016 -0500 @@ -60,6 +60,20 @@ o 0 +(display nodes filtered by log options) + + $ hg log -G -r 'sort(all(), topo)' -k '.3' + o 8 + | + o 3 + | + ~ + o 7 + | + o 6 + | + ~ + (revset skipping nodes) $ hg log -G --rev 'sort(not (2+6), topo)'
--- a/tests/test-glog.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-glog.t Tue Oct 18 14:15:15 2016 -0500 @@ -1455,12 +1455,13 @@ (group (group (or - (func - ('symbol', 'user') - ('string', 'test')) - (func - ('symbol', 'user') - ('string', 'not-a-user'))))) + (list + (func + ('symbol', 'user') + ('string', 'test')) + (func + ('symbol', 'user') + ('string', 'not-a-user')))))) $ testlog -b not-a-branch abort: unknown revision 'not-a-branch'! abort: unknown revision 'not-a-branch'! @@ -1470,26 +1471,28 @@ (group (group (or - (func - ('symbol', 'branch') - ('string', 'default')) - (func - ('symbol', 'branch') - ('string', 'branch')) - (func - ('symbol', 'branch') - ('string', 'branch'))))) + (list + (func + ('symbol', 'branch') + ('string', 'default')) + (func + ('symbol', 'branch') + ('string', 'branch')) + (func + ('symbol', 'branch') + ('string', 'branch')))))) $ testlog -k expand -k merge [] (group (group (or - (func - ('symbol', 'keyword') - ('string', 'expand')) - (func - ('symbol', 'keyword') - ('string', 'merge'))))) + (list + (func + ('symbol', 'keyword') + ('string', 'expand')) + (func + ('symbol', 'keyword') + ('string', 'merge')))))) $ testlog --only-merges [] (group @@ -1520,17 +1523,19 @@ (not (group (or - ('string', '31') - (func - ('symbol', 'ancestors') - ('string', '31'))))) + (list + ('string', '31') + (func + ('symbol', 'ancestors') + ('string', '31')))))) (not (group (or - ('string', '32') - (func - ('symbol', 'ancestors') - ('string', '32')))))))) + (list + ('string', '32') + (func + ('symbol', 'ancestors') + ('string', '32'))))))))) Dedicated repo for --follow and paths filtering. The g is crafted to have 2 filelog topological heads in a linear changeset graph. @@ -1587,12 +1592,13 @@ (group (group (or - (func - ('symbol', 'filelog') - ('string', 'a')) - (func - ('symbol', 'filelog') - ('string', 'b'))))) + (list + (func + ('symbol', 'filelog') + ('string', 'a')) + (func + ('symbol', 'filelog') + ('string', 'b')))))) Test falling back to slow path for non-existing files @@ -1744,12 +1750,13 @@ (group (group (or - (func - ('symbol', 'follow') - ('string', 'g')) - (func - ('symbol', 'follow') - ('string', 'e'))))) + (list + (func + ('symbol', 'follow') + ('string', 'g')) + (func + ('symbol', 'follow') + ('string', 'e')))))) $ cat log.nodes nodetag 4 nodetag 3
--- a/tests/test-gpg.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-gpg.t Tue Oct 18 14:15:15 2016 -0500 @@ -7,8 +7,25 @@ > gpg= > > [gpg] - > cmd=gpg --no-permission-warning --no-secmem-warning --no-auto-check-trustdb --homedir "$TESTDIR/gpg" + > cmd=gpg --no-permission-warning --no-secmem-warning --no-auto-check-trustdb > EOF + $ GNUPGHOME="$TESTTMP/gpg"; export GNUPGHOME + $ cp -R "$TESTDIR/gpg" "$GNUPGHOME" + +Start gpg-agent, which is required by GnuPG v2 + +#if gpg21 + $ gpg-connect-agent -q --subst /serverpid '/echo ${get serverpid}' /bye \ + > >> $DAEMON_PIDS +#endif + +and migrate secret keys + +#if gpg2 + $ gpg --no-permission-warning --no-secmem-warning --list-secret-keys \ + > > /dev/null 2>&1 +#endif + $ hg init r $ cd r $ echo foo > foo @@ -36,12 +53,4 @@ e63c23eaa88a is signed by: hgtest -verify that this test has not modified the trustdb.gpg file back in -the main hg working dir - $ md5sum.py "$TESTDIR/gpg/trustdb.gpg" - f6b9c78c65fa9536e7512bb2ceb338ae */gpg/trustdb.gpg (glob) - -don't leak any state to next test run - $ rm -f "$TESTDIR/gpg/random_seed" - $ cd ..
--- a/tests/test-graft.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-graft.t Tue Oct 18 14:15:15 2016 -0500 @@ -179,6 +179,11 @@ committing changelog grafting 5:97f8bfe72746 "5" searching for copies back to rev 1 + unmatched files in other (from topological common ancestor): + c + all copies found (* = to merge, ! = divergent, % = renamed and deleted): + src: 'c' -> dst: 'b' * + checking for directory renames resolving manifests branchmerge: True, force: True, partial: False ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746 @@ -193,6 +198,11 @@ scanning for duplicate grafts grafting 4:9c233e8e184d "4" searching for copies back to rev 1 + unmatched files in other (from topological common ancestor): + c + all copies found (* = to merge, ! = divergent, % = renamed and deleted): + src: 'c' -> dst: 'b' * + checking for directory renames resolving manifests branchmerge: True, force: True, partial: False ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d @@ -253,7 +263,7 @@ $ hg graft -c grafting 4:9c233e8e184d "4" - abort: unresolved merge conflicts (see "hg help resolve") + abort: unresolved merge conflicts (see 'hg help resolve') [255] Fix up: @@ -427,8 +437,8 @@ $ hg graft 3 --log -u foo grafting 3:4c60f11aa304 "3" warning: can't find ancestor for 'c' copied from 'b'! - $ hg log --template '{rev} {parents} {desc}\n' -r tip - 14 1:5d205f8b35b6 3 + $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip + 14:0c921c65ef1e 1:5d205f8b35b6 3 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8) Resolve conflicted graft @@ -620,7 +630,7 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: 2 - changeset: 14:f64defefacee + changeset: 14:0c921c65ef1e parent: 1:5d205f8b35b6 user: foo date: Thu Jan 01 00:00:00 1970 +0000 @@ -842,3 +852,431 @@ |/ o 0 +Graft from behind a move or rename +================================== + +NOTE: This is affected by issue5343, and will need updating when it's fixed + +Possible cases during a regular graft (when ca is between cta and c2): + +name | c1<-cta | cta<->ca | ca->c2 +A.0 | | | +A.1 | X | | +A.2 | | X | +A.3 | | | X +A.4 | X | X | +A.5 | X | | X +A.6 | | X | X +A.7 | X | X | X + +A.0 is trivial, and doesn't need copy tracking. +For A.1, a forward rename is recorded in the c1 pass, to be followed later. +In A.2, the rename is recorded in the c2 pass and followed backwards. +A.3 is recorded in the c2 pass as a forward rename to be duplicated on target. +In A.4, both passes of checkcopies record incomplete renames, which are +then joined in mergecopies to record a rename to be followed. +In A.5 and A.7, the c1 pass records an incomplete rename, while the c2 pass +records an incomplete divergence. The incomplete rename is then joined to the +appropriate side of the incomplete divergence, and the result is recorded as a +divergence. The code doesn't distinguish at all between these two cases, since +the end result of them is the same: an incomplete divergence joined with an +incomplete rename into a divergence. +Finally, A.6 records a divergence entirely in the c2 pass. + +A.4 has a degenerate case a<-b<-a->a, where checkcopies isn't needed at all. +A.5 has a special case a<-b<-b->a, which is treated like a<-b->a in a merge. +A.6 has a special case a<-a<-b->a. Here, checkcopies will find a spurious +incomplete divergence, which is in fact complete. This is handled later in +mergecopies. +A.7 has 4 special cases: a<-b<-a->b (the "ping-pong" case), a<-b<-c->b, +a<-b<-a->c and a<-b<-c->a. Of these, only the "ping-pong" case is interesting, +the others are fairly trivial (a<-b<-c->b and a<-b<-a->c proceed like the base +case, a<-b<-c->a is treated the same as a<-b<-b->a). + +f5a therefore tests the "ping-pong" rename case, where a file is renamed to the +same name on both branches, then the rename is backed out on one branch, and +the backout is grafted to the other branch. This creates a challenging rename +sequence of a<-b<-a->b in the graft target, topological CA, graft CA and graft +source, respectively. Since rename detection will run on the c1 side for such a +sequence (as for technical reasons, we split the c1 and c2 sides not at the +graft CA, but rather at the topological CA), it will pick up a false rename, +and cause a spurious merge conflict. This false rename is always exactly the +reverse of the true rename that would be detected on the c2 side, so we can +correct for it by detecting this condition and reversing as necessary. + +First, set up the repository with commits to be grafted + + $ hg init ../graftmove + $ cd ../graftmove + $ echo c1a > f1a + $ echo c2a > f2a + $ echo c3a > f3a + $ echo c4a > f4a + $ echo c5a > f5a + $ hg ci -qAm A0 + $ hg mv f1a f1b + $ hg mv f3a f3b + $ hg mv f5a f5b + $ hg ci -qAm B0 + $ echo c1c > f1b + $ hg mv f2a f2c + $ hg mv f5b f5a + $ echo c5c > f5a + $ hg ci -qAm C0 + $ hg mv f3b f3d + $ echo c4d > f4a + $ hg ci -qAm D0 + $ hg log -G + @ changeset: 3:b69f5839d2d9 + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: D0 + | + o changeset: 2:f58c7e2b28fa + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: C0 + | + o changeset: 1:3d7bba921b5d + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: B0 + | + o changeset: 0:11f7a1b56675 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: A0 + + +Test the cases A.2 (f1x), A.3 (f2x) and a special case of A.6 (f5x) where the +two renames actually converge to the same name (thus no actual divergence). + + $ hg up -q 'desc("A0")' + $ HGEDITOR="echo C1 >" hg graft -r 'desc("C0")' --edit + grafting 2:f58c7e2b28fa "C0" + merging f1a and f1b to f1a + merging f5a + warning: can't find ancestor for 'f5a' copied from 'f5b'! + $ hg status --change . + M f1a + M f5a + A f2c + R f2a + $ hg cat f1a + c1c + $ hg cat f1b + f1b: no such file in rev c9763722f9bd + [1] + +Test the cases A.0 (f4x) and A.6 (f3x) + + $ HGEDITOR="echo D1 >" hg graft -r 'desc("D0")' --edit + grafting 3:b69f5839d2d9 "D0" + note: possible conflict - f3b was renamed multiple times to: + f3d + f3a + warning: can't find ancestor for 'f3d' copied from 'f3b'! + +Set up the repository for some further tests + + $ hg up -q "min(desc("A0"))" + $ hg mv f1a f1e + $ echo c2e > f2a + $ hg mv f3a f3e + $ hg mv f4a f4e + $ hg mv f5a f5b + $ hg ci -qAm "E0" + $ hg log -G + @ changeset: 6:6bd1736cab86 + | tag: tip + | parent: 0:11f7a1b56675 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: E0 + | + | o changeset: 5:560daee679da + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: D1 + | | + | o changeset: 4:c9763722f9bd + |/ parent: 0:11f7a1b56675 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: C1 + | + | o changeset: 3:b69f5839d2d9 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: D0 + | | + | o changeset: 2:f58c7e2b28fa + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | summary: C0 + | | + | o changeset: 1:3d7bba921b5d + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: B0 + | + o changeset: 0:11f7a1b56675 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: A0 + + +Test the cases A.4 (f1x), the "ping-pong" special case of A.7 (f5x), +and A.3 with a local content change to be preserved (f2x). + + $ HGEDITOR="echo C2 >" hg graft -r 'desc("C0")' --edit + grafting 2:f58c7e2b28fa "C0" + merging f1e and f1b to f1e + merging f2a and f2c to f2c + merging f5b and f5a to f5a + +Test the cases A.1 (f4x) and A.7 (f3x). + + $ HGEDITOR="echo D2 >" hg graft -r 'desc("D0")' --edit + grafting 3:b69f5839d2d9 "D0" + note: possible conflict - f3b was renamed multiple times to: + f3e + f3d + merging f4e and f4a to f4e + warning: can't find ancestor for 'f3d' copied from 'f3b'! + +Check the results of the grafts tested + + $ hg log -CGv --patch --git + @ changeset: 8:93ee502e8b0a + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | files: f3d f4e + | description: + | D2 + | + | + | diff --git a/f3d b/f3d + | new file mode 100644 + | --- /dev/null + | +++ b/f3d + | @@ -0,0 +1,1 @@ + | +c3a + | diff --git a/f4e b/f4e + | --- a/f4e + | +++ b/f4e + | @@ -1,1 +1,1 @@ + | -c4a + | +c4d + | + o changeset: 7:539cf145f496 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | files: f1e f2a f2c f5a f5b + | copies: f2c (f2a) f5a (f5b) + | description: + | C2 + | + | + | diff --git a/f1e b/f1e + | --- a/f1e + | +++ b/f1e + | @@ -1,1 +1,1 @@ + | -c1a + | +c1c + | diff --git a/f2a b/f2c + | rename from f2a + | rename to f2c + | diff --git a/f5b b/f5a + | rename from f5b + | rename to f5a + | --- a/f5b + | +++ b/f5a + | @@ -1,1 +1,1 @@ + | -c5a + | +c5c + | + o changeset: 6:6bd1736cab86 + | parent: 0:11f7a1b56675 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | files: f1a f1e f2a f3a f3e f4a f4e f5a f5b + | copies: f1e (f1a) f3e (f3a) f4e (f4a) f5b (f5a) + | description: + | E0 + | + | + | diff --git a/f1a b/f1e + | rename from f1a + | rename to f1e + | diff --git a/f2a b/f2a + | --- a/f2a + | +++ b/f2a + | @@ -1,1 +1,1 @@ + | -c2a + | +c2e + | diff --git a/f3a b/f3e + | rename from f3a + | rename to f3e + | diff --git a/f4a b/f4e + | rename from f4a + | rename to f4e + | diff --git a/f5a b/f5b + | rename from f5a + | rename to f5b + | + | o changeset: 5:560daee679da + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | files: f3d f4a + | | description: + | | D1 + | | + | | + | | diff --git a/f3d b/f3d + | | new file mode 100644 + | | --- /dev/null + | | +++ b/f3d + | | @@ -0,0 +1,1 @@ + | | +c3a + | | diff --git a/f4a b/f4a + | | --- a/f4a + | | +++ b/f4a + | | @@ -1,1 +1,1 @@ + | | -c4a + | | +c4d + | | + | o changeset: 4:c9763722f9bd + |/ parent: 0:11f7a1b56675 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | files: f1a f2a f2c f5a + | copies: f2c (f2a) + | description: + | C1 + | + | + | diff --git a/f1a b/f1a + | --- a/f1a + | +++ b/f1a + | @@ -1,1 +1,1 @@ + | -c1a + | +c1c + | diff --git a/f2a b/f2c + | rename from f2a + | rename to f2c + | diff --git a/f5a b/f5a + | --- a/f5a + | +++ b/f5a + | @@ -1,1 +1,1 @@ + | -c5a + | +c5c + | + | o changeset: 3:b69f5839d2d9 + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | files: f3b f3d f4a + | | copies: f3d (f3b) + | | description: + | | D0 + | | + | | + | | diff --git a/f3b b/f3d + | | rename from f3b + | | rename to f3d + | | diff --git a/f4a b/f4a + | | --- a/f4a + | | +++ b/f4a + | | @@ -1,1 +1,1 @@ + | | -c4a + | | +c4d + | | + | o changeset: 2:f58c7e2b28fa + | | user: test + | | date: Thu Jan 01 00:00:00 1970 +0000 + | | files: f1b f2a f2c f5a f5b + | | copies: f2c (f2a) f5a (f5b) + | | description: + | | C0 + | | + | | + | | diff --git a/f1b b/f1b + | | --- a/f1b + | | +++ b/f1b + | | @@ -1,1 +1,1 @@ + | | -c1a + | | +c1c + | | diff --git a/f2a b/f2c + | | rename from f2a + | | rename to f2c + | | diff --git a/f5b b/f5a + | | rename from f5b + | | rename to f5a + | | --- a/f5b + | | +++ b/f5a + | | @@ -1,1 +1,1 @@ + | | -c5a + | | +c5c + | | + | o changeset: 1:3d7bba921b5d + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | files: f1a f1b f3a f3b f5a f5b + | copies: f1b (f1a) f3b (f3a) f5b (f5a) + | description: + | B0 + | + | + | diff --git a/f1a b/f1b + | rename from f1a + | rename to f1b + | diff --git a/f3a b/f3b + | rename from f3a + | rename to f3b + | diff --git a/f5a b/f5b + | rename from f5a + | rename to f5b + | + o changeset: 0:11f7a1b56675 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + files: f1a f2a f3a f4a f5a + description: + A0 + + + diff --git a/f1a b/f1a + new file mode 100644 + --- /dev/null + +++ b/f1a + @@ -0,0 +1,1 @@ + +c1a + diff --git a/f2a b/f2a + new file mode 100644 + --- /dev/null + +++ b/f2a + @@ -0,0 +1,1 @@ + +c2a + diff --git a/f3a b/f3a + new file mode 100644 + --- /dev/null + +++ b/f3a + @@ -0,0 +1,1 @@ + +c3a + diff --git a/f4a b/f4a + new file mode 100644 + --- /dev/null + +++ b/f4a + @@ -0,0 +1,1 @@ + +c4a + diff --git a/f5a b/f5a + new file mode 100644 + --- /dev/null + +++ b/f5a + @@ -0,0 +1,1 @@ + +c5a + + $ hg cat f2c + c2e
--- a/tests/test-grep.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-grep.t Tue Oct 18 14:15:15 2016 -0500 @@ -40,6 +40,61 @@ \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mva\x1b[0;31;1mport\x1b[0might (esc) \x1b[0;35mport\x1b[0m\x1b[0;36m:\x1b[0m\x1b[0;32m4\x1b[0m\x1b[0;36m:\x1b[0mim\x1b[0;31;1mport\x1b[0m/ex\x1b[0;31;1mport\x1b[0m (esc) +simple templated + + $ hg grep port \ + > -T '{file}:{rev}:{node|short}:{texts % "{if(matched, text|upper, text)}"}\n' + port:4:914fa752cdea:exPORT + port:4:914fa752cdea:vaPORTight + port:4:914fa752cdea:imPORT/exPORT + +simple JSON (no "change" field) + + $ hg grep -Tjson port + [ + { + "date": [4.0, 0], + "file": "port", + "line_number": 1, + "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c", + "rev": 4, + "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}], + "user": "spam" + }, + { + "date": [4.0, 0], + "file": "port", + "line_number": 2, + "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c", + "rev": 4, + "texts": [{"matched": false, "text": "va"}, {"matched": true, "text": "port"}, {"matched": false, "text": "ight"}], + "user": "spam" + }, + { + "date": [4.0, 0], + "file": "port", + "line_number": 3, + "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c", + "rev": 4, + "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}], + "user": "spam" + } + ] + +simple JSON without matching lines + + $ hg grep -Tjson -l port + [ + { + "date": [4.0, 0], + "file": "port", + "line_number": 1, + "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c", + "rev": 4, + "user": "spam" + } + ] + all $ hg grep --traceback --all -nu port port @@ -53,6 +108,102 @@ port:1:2:+:eggs:export port:0:1:+:spam:import +all JSON + + $ hg grep --all -Tjson port port + [ + { + "change": "-", + "date": [4.0, 0], + "file": "port", + "line_number": 4, + "node": "914fa752cdea87777ac1a8d5c858b0c736218f6c", + "rev": 4, + "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}], + "user": "spam" + }, + { + "change": "+", + "date": [3.0, 0], + "file": "port", + "line_number": 4, + "node": "95040cfd017d658c536071c6290230a613c4c2a6", + "rev": 3, + "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}], + "user": "eggs" + }, + { + "change": "-", + "date": [2.0, 0], + "file": "port", + "line_number": 1, + "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47", + "rev": 2, + "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}], + "user": "spam" + }, + { + "change": "-", + "date": [2.0, 0], + "file": "port", + "line_number": 2, + "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47", + "rev": 2, + "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}], + "user": "spam" + }, + { + "change": "+", + "date": [2.0, 0], + "file": "port", + "line_number": 1, + "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47", + "rev": 2, + "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}], + "user": "spam" + }, + { + "change": "+", + "date": [2.0, 0], + "file": "port", + "line_number": 2, + "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47", + "rev": 2, + "texts": [{"matched": false, "text": "va"}, {"matched": true, "text": "port"}, {"matched": false, "text": "ight"}], + "user": "spam" + }, + { + "change": "+", + "date": [2.0, 0], + "file": "port", + "line_number": 3, + "node": "3b325e3481a1f07435d81dfdbfa434d9a0245b47", + "rev": 2, + "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}, {"matched": false, "text": "/ex"}, {"matched": true, "text": "port"}], + "user": "spam" + }, + { + "change": "+", + "date": [1.0, 0], + "file": "port", + "line_number": 2, + "node": "8b20f75c158513ff5ac80bd0e5219bfb6f0eb587", + "rev": 1, + "texts": [{"matched": false, "text": "ex"}, {"matched": true, "text": "port"}], + "user": "eggs" + }, + { + "change": "+", + "date": [0.0, 0], + "file": "port", + "line_number": 1, + "node": "f31323c9217050ba245ee8b537c713ec2e8ab226", + "rev": 0, + "texts": [{"matched": false, "text": "im"}, {"matched": true, "text": "port"}], + "user": "spam" + } + ] + other $ hg grep -l port port @@ -111,6 +262,12 @@ color:2:-:orange color:1:+:orange +test substring match: '^' should only match at the beginning + + $ hg grep '^.' --config extensions.color= --color debug + [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|b]lack + [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|o]range + [grep.filename|color][grep.sep|:][grep.rev|3][grep.sep|:][grep.match|b]lue match in last "line" without newline
--- a/tests/test-help.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-help.t Tue Oct 18 14:15:15 2016 -0500 @@ -23,7 +23,7 @@ summary summarize working directory state update update working directory (or switch revisions) - (use "hg help" for the full list of commands or "hg -v" for details) + (use 'hg help' for the full list of commands or 'hg -v' for details) $ hg -q add add the specified files on the next commit @@ -123,7 +123,7 @@ templating Template Usage urls URL Paths - (use "hg help -v" to show built-in aliases and global options) + (use 'hg help -v' to show built-in aliases and global options) $ hg -q help add add the specified files on the next commit @@ -332,7 +332,7 @@ -h --help display help and exit --hidden consider hidden changesets - (use "hg help" for the full list of commands) + (use 'hg help' for the full list of commands) $ hg add -h hg add [OPTION]... [FILE]... @@ -467,7 +467,7 @@ -S --subrepos recurse into subrepositories -n --dry-run do not perform actions, just print output - (use "hg add -h" to show more help) + (use 'hg add -h' to show more help) [255] Test ambiguous command help @@ -478,7 +478,7 @@ add add the specified files on the next commit addremove add all new files, delete all missing files - (use "hg help -v ad" to show built-in aliases and global options) + (use 'hg help -v ad' to show built-in aliases and global options) Test command without options @@ -622,7 +622,7 @@ $ hg help foo abort: no such help topic: foo - (try "hg help --keyword foo") + (try 'hg help --keyword foo') [255] $ hg skjdfks @@ -649,7 +649,7 @@ summary summarize working directory state update update working directory (or switch revisions) - (use "hg help" for the full list of commands or "hg -v" for details) + (use 'hg help' for the full list of commands or 'hg -v' for details) [255] @@ -842,7 +842,7 @@ templating Template Usage urls URL Paths - (use "hg help -v" to show built-in aliases and global options) + (use 'hg help -v' to show built-in aliases and global options) Test list of internal help commands @@ -921,7 +921,7 @@ debugwireargs (no help text available) - (use "hg help -v debug" to show built-in aliases and global options) + (use 'hg help -v debug' to show built-in aliases and global options) internals topic renders index of available sub-topics @@ -929,16 +929,17 @@ Technical implementation topics """"""""""""""""""""""""""""""" - bundles container for exchange of repository data - changegroups representation of revlog data - requirements repository requirements - revlogs revision storage mechanism + bundles Bundles + changegroups Changegroups + requirements Repository Requirements + revlogs Revision Logs + wireprotocol Wire Protocol sub-topics can be accessed $ hg help internals.changegroups - Changegroups - ============ + Changegroups + """""""""""" Changegroups are representations of repository revlog data, specifically the changelog, manifest, and filelogs. @@ -974,7 +975,7 @@ this an *empty chunk*. Delta Groups - ------------ + ============ A *delta group* expresses the content of a revlog as a series of deltas, or patches against previous revisions. @@ -1050,21 +1051,21 @@ which can result in smaller deltas and more efficient encoding of data. Changeset Segment - ----------------- + ================= The *changeset segment* consists of a single *delta group* holding changelog data. It is followed by an *empty chunk* to denote the boundary to the *manifests segment*. Manifest Segment - ---------------- + ================ The *manifest segment* consists of a single *delta group* holding manifest data. It is followed by an *empty chunk* to denote the boundary to the *filelogs segment*. Filelogs Segment - ---------------- + ================ The *filelogs* segment consists of multiple sub-segments, each corresponding to an individual file whose data is being described: @@ -1103,7 +1104,7 @@ nohelp (no help text available) - (use "hg help -v helpext" to show built-in aliases and global options) + (use 'hg help -v helpext' to show built-in aliases and global options) test deprecated and experimental options are hidden in command help @@ -1249,12 +1250,12 @@ $ hg help config.`hg help config|grep '^ "'| \ > tail -1|sed 's![ "]*!!g'`| \ - > grep "hg help -c config" > /dev/null + > grep 'hg help -c config' > /dev/null [1] note to use help -c for general hg help config: - $ hg help config |grep "hg help -c config" > /dev/null + $ hg help config |grep 'hg help -c config' > /dev/null Test templating help @@ -1329,7 +1330,7 @@ Extension Commands: $ hg help -c schemes abort: no such help topic: schemes - (try "hg help --keyword schemes") + (try 'hg help --keyword schemes') [255] $ hg help -e schemes |head -1 schemes extension - extend schemes with shortcuts to repository swarms @@ -1344,7 +1345,7 @@ $ hg help -e -c commit > /dev/null $ hg help -e commit > /dev/null abort: no such help topic: commit - (try "hg help --keyword commit") + (try 'hg help --keyword commit') [255] Test keyword search help @@ -1386,14 +1387,14 @@ $ hg help nonexistingtopicthatwillneverexisteverever abort: no such help topic: nonexistingtopicthatwillneverexisteverever - (try "hg help --keyword nonexistingtopicthatwillneverexisteverever") + (try 'hg help --keyword nonexistingtopicthatwillneverexisteverever') [255] Test unfound keyword $ hg help --keyword nonexistingwordthatwillneverexisteverever abort: no matches - (try "hg help" for a list of topics) + (try 'hg help' for a list of topics) [255] Test omit indicating for help @@ -1550,6 +1551,9 @@ $ hg help template.files files List of strings. All files modified, added, or removed by this changeset. + files(pattern) + All files of the current changeset matching the pattern. See + 'hg help patterns'. Test section lookup by translated message @@ -1588,7 +1592,7 @@ > subsequent section > ------------------ > - > This should be hidden at "hg help ambiguous" with section name. + > This should be hidden at 'hg help ambiguous' with section name. > ''' > """ % (escape(upper), escape(lower))) > EOF @@ -1623,6 +1627,17 @@ > ambiguous = ! > EOF +Show help content of disabled extensions + + $ cat >> $HGRCPATH <<EOF + > [extensions] + > ambiguous = !./ambiguous.py + > EOF + $ hg help -e ambiguous + ambiguous extension - (no help text available) + + (use 'hg help extensions' for information on enabling extensions) + Test dynamic list of merge tools only shows up once $ hg help merge-tools Merge Tools @@ -1813,7 +1828,7 @@ number or hash, or <a href="/help/revsets">revset expression</a>.</div> </form> <table class="bigtable"> - <tr><td colspan="2"><h2><a name="main" href="#topics">Topics</a></h2></td></tr> + <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr> <tr><td> <a href="/help/config"> @@ -2865,35 +2880,42 @@ number or hash, or <a href="/help/revsets">revset expression</a>.</div> </form> <table class="bigtable"> - <tr><td colspan="2"><h2><a name="main" href="#topics">Topics</a></h2></td></tr> + <tr><td colspan="2"><h2><a name="topics" href="#topics">Topics</a></h2></td></tr> <tr><td> <a href="/help/internals.bundles"> bundles </a> </td><td> - container for exchange of repository data + Bundles </td></tr> <tr><td> <a href="/help/internals.changegroups"> changegroups </a> </td><td> - representation of revlog data + Changegroups </td></tr> <tr><td> <a href="/help/internals.requirements"> requirements </a> </td><td> - repository requirements + Repository Requirements </td></tr> <tr><td> <a href="/help/internals.revlogs"> revlogs </a> </td><td> - revision storage mechanism + Revision Logs + </td></tr> + <tr><td> + <a href="/help/internals.wireprotocol"> + wireprotocol + </a> + </td><td> + Wire Protocol </td></tr> @@ -2957,8 +2979,7 @@ number or hash, or <a href="/help/revsets">revset expression</a>.</div> </form> <div id="doc"> - <h1>representation of revlog data</h1> - <h2>Changegroups</h2> + <h1>Changegroups</h1> <p> Changegroups are representations of repository revlog data, specifically the changelog, manifest, and filelogs. @@ -3000,7 +3021,7 @@ There is a special case chunk that has 0 length ("0x00000000"). We call this an *empty chunk*. </p> - <h3>Delta Groups</h3> + <h2>Delta Groups</h2> <p> A *delta group* expresses the content of a revlog as a series of deltas, or patches against previous revisions. @@ -3091,19 +3112,19 @@ changegroup. This allows the delta to be expressed against any parent, which can result in smaller deltas and more efficient encoding of data. </p> - <h3>Changeset Segment</h3> + <h2>Changeset Segment</h2> <p> The *changeset segment* consists of a single *delta group* holding changelog data. It is followed by an *empty chunk* to denote the boundary to the *manifests segment*. </p> - <h3>Manifest Segment</h3> + <h2>Manifest Segment</h2> <p> The *manifest segment* consists of a single *delta group* holding manifest data. It is followed by an *empty chunk* to denote the boundary to the *filelogs segment*. </p> - <h3>Filelogs Segment</h3> + <h2>Filelogs Segment</h2> <p> The *filelogs* segment consists of multiple sub-segments, each corresponding to an individual file whose data is being described:
--- a/tests/test-hgrc.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-hgrc.t Tue Oct 18 14:15:15 2016 -0500 @@ -28,12 +28,12 @@ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cd foobar $ cat .hg/hgrc - # example repository config (see "hg help config" for more info) + # example repository config (see 'hg help config' for more info) [paths] default = $TESTTMP/foo%bar (glob) # path aliases to other clones of this repo in URLs or filesystem paths - # (see "hg help config.paths" for more info) + # (see 'hg help config.paths' for more info) # # default-push = ssh://jdoe@example.net/hg/jdoes-fork # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
--- a/tests/test-hgweb-commands.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-hgweb-commands.t Tue Oct 18 14:15:15 2016 -0500 @@ -1687,7 +1687,7 @@ </td> </tr> <tr class="light"> - <td colspan="4"><a class="list" href="/branches?style=gitweb">...</a></td> + <td colspan="3"><a class="list" href="/branches?style=gitweb">...</a></td> </tr> </table> <script type="text/javascript">process_dates()</script> @@ -1965,6 +1965,9 @@ .annotate { font-size: smaller; text-align: right; padding-right: 1em; } tr.thisrev a { color:#999999; text-decoration: none; } tr.thisrev pre { color:#009900; } + td.annotate { + white-space: nowrap; + } div.annotate-info { display: none; position: absolute;
--- a/tests/test-hgweb.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-hgweb.t Tue Oct 18 14:15:15 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: 6947 + content-length: 6986 content-type: text/css body { font-family: sans-serif; font-size: 12px; border:solid #d9d8d1; border-width:1px; margin:10px; background: white; color: black; } @@ -400,6 +400,9 @@ div.search { margin:4px 8px; position:absolute; top:56px; right:12px } tr.thisrev a { color:#999999; text-decoration: none; } tr.thisrev pre { color:#009900; } + td.annotate { + white-space: nowrap; + } div.annotate-info { display: none; position: absolute;
--- a/tests/test-histedit-arguments.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-histedit-arguments.t Tue Oct 18 14:15:15 2016 -0500 @@ -169,7 +169,7 @@ > pick 08d98a8350f3 4 five > EOF hg: parse error: missing rules for changeset c8e68270e35a - (use "drop c8e68270e35a" to discard, see also: "hg help -e histedit.config") + (use "drop c8e68270e35a" to discard, see also: 'hg help -e histedit.config') [255] Test that extra revisions are detected
--- a/tests/test-histedit-base.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-histedit-base.t Tue Oct 18 14:15:15 2016 -0500 @@ -235,8 +235,8 @@ > base d273e35dcdf2 B > pick b2f90fd8aa85 I > EOF - hg: parse error: base "d273e35dcdf2" changeset was not an edited list candidate - (only use listed changesets) + hg: parse error: base "d273e35dcdf2" changeset was an edited list candidate + (base must only use unlisted changesets) $ hg --config experimental.histeditng=False histedit 5 --commands - 2>&1 << EOF | fixbundle > base cd010b8cd998 A
--- a/tests/test-histedit-drop.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-histedit-drop.t Tue Oct 18 14:15:15 2016 -0500 @@ -151,7 +151,7 @@ > pick ee283cb5f2d5 e > EOF hg: parse error: missing rules for changeset a4f7421b80f7 - (use "drop a4f7421b80f7" to discard, see also: "hg help -e histedit.config") + (use "drop a4f7421b80f7" to discard, see also: 'hg help -e histedit.config') $ hg --config histedit.dropmissing=True histedit cb9a9f314b8b --commands - 2>&1 << EOF | fixbundle > EOF hg: parse error: no rules provided
--- a/tests/test-histedit-obsolete.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-histedit-obsolete.t Tue Oct 18 14:15:15 2016 -0500 @@ -299,7 +299,7 @@ $ hg histedit -r '.~2' abort: cannot edit public changeset: cb9a9f314b8b - (see "hg help phases" for details) + (see 'hg help phases' for details) [255]
--- a/tests/test-histedit-outgoing.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-histedit-outgoing.t Tue Oct 18 14:15:15 2016 -0500 @@ -130,7 +130,7 @@ $ HGEDITOR=cat hg -q histedit --outgoing '../r' abort: there are ambiguous outgoing revisions - (see "hg help histedit" for more detail) + (see 'hg help histedit' for more detail) [255] $ hg -q update -C 2 @@ -147,7 +147,7 @@ $ HGEDITOR=cat hg -q histedit --outgoing '../r#default' abort: there are ambiguous outgoing revisions - (see "hg help histedit" for more detail) + (see 'hg help histedit' for more detail) [255] $ cd ..
--- a/tests/test-hook.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-hook.t Tue Oct 18 14:15:15 2016 -0500 @@ -1,12 +1,6 @@ commit hooks can see env vars (and post-transaction one are run unlocked) - $ cat << EOF >> $HGRCPATH - > [experimental] - > # drop me once bundle2 is the default, - > # added to get test change early. - > bundle2-exp = True - > EOF $ cat > $TESTTMP/txnabort.checkargs.py <<EOF > def showargs(ui, repo, hooktype, **kwargs): @@ -808,7 +802,7 @@ saved backup bundle to * (glob) transaction abort! rollback completed - strip failed, full bundle stored in * (glob) + strip failed, backup bundle stored in * (glob) abort: pretxnclose.error hook exited with status 1 [255] $ hg recover
--- a/tests/test-http-bundle1.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-http-bundle1.t Tue Oct 18 14:15:15 2016 -0500 @@ -4,9 +4,9 @@ parts that are not bundle1/bundle2 specific. $ cat << EOF >> $HGRCPATH - > [experimental] + > [devel] > # This test is dedicated to interaction through old bundle - > bundle2-exp = False + > legacy.exchange = bundle1 > EOF $ hg init test
--- a/tests/test-http-proxy.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-http-proxy.t Tue Oct 18 14:15:15 2016 -0500 @@ -1,10 +1,4 @@ #require serve - $ cat << EOF >> $HGRCPATH - > [experimental] - > # drop me once bundle2 is the default, - > # added to get test change early. - > bundle2-exp = True - > EOF $ hg init a $ cd a
--- a/tests/test-https.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-https.t Tue Oct 18 14:15:15 2016 -0500 @@ -230,7 +230,7 @@ $ cp copy-pull/.hg/hgrc copy-pull/.hg/hgrc.bu $ echo "[web]" >> copy-pull/.hg/hgrc $ echo "cacerts=$CERTSDIR/pub.pem" >> copy-pull/.hg/hgrc - $ hg -R copy-pull pull --traceback + $ hg -R copy-pull pull pulling from https://localhost:$HGPORT/ warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?) searching for changes @@ -554,7 +554,7 @@ Test unvalidated https through proxy - $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure --traceback + $ http_proxy=http://localhost:$HGPORT1/ hg -R copy-pull pull --insecure pulling from https://localhost:$HGPORT/ warning: connecting to localhost using legacy security technology (TLS 1.0); see https://mercurial-scm.org/wiki/SecureConnections for more info (?) warning: connection security to localhost is disabled per current settings; communication is susceptible to eavesdropping and tampering
--- a/tests/test-import-bypass.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-import-bypass.t Tue Oct 18 14:15:15 2016 -0500 @@ -41,6 +41,7 @@ $ hg import --bypass ../test.diff applying ../test.diff unable to find 'a' for patching + (use '--prefix' to apply patch relative to the current directory) abort: patch failed to apply [255] $ hg st
--- a/tests/test-import.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-import.t Tue Oct 18 14:15:15 2016 -0500 @@ -1623,6 +1623,7 @@ $ hg export --rev 'desc("extended jungle")' | hg import --partial - applying patch from stdin unable to find 'jungle' for patching + (use '--prefix' to apply patch relative to the current directory) 1 out of 1 hunks FAILED -- saving rejects to file jungle.rej patch applied partially (fix the .rej files and run `hg commit --amend`) @@ -1764,3 +1765,41 @@ $ hg log --debug -r . | grep extra extra: branch=default extra: foo=bar + +Warn the user that paths are relative to the root of +repository when file not found for patching + + $ mkdir filedir + $ echo "file1" >> filedir/file1 + $ hg add filedir/file1 + $ hg commit -m "file1" + $ cd filedir + $ hg import -p 2 - <<EOF + > # HG changeset patch + > # User test + > # Date 0 0 + > file2 + > + > diff --git a/filedir/file1 b/filedir/file1 + > --- a/filedir/file1 + > +++ b/filedir/file1 + > @@ -1,1 +1,2 @@ + > file1 + > +file2 + > EOF + applying patch from stdin + unable to find 'file1' for patching + (use '--prefix' to apply patch relative to the current directory) + 1 out of 1 hunks FAILED -- saving rejects to file file1.rej + abort: patch failed to apply + [255] + +test import crash (issue5375) + $ cd .. + $ hg init repo + $ cd repo + $ printf "diff --git a/a b/b\nrename from a\nrename to b" | hg import - + applying patch from stdin + a not tracked! + abort: source file 'a' does not exist + [255]
--- a/tests/test-issue1175.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-issue1175.t Tue Oct 18 14:15:15 2016 -0500 @@ -54,7 +54,7 @@ diff --git a/b b/b new file mode 100644 -http://bz.selenic.com/show_bug.cgi?id=4476 +https://bz.mercurial-scm.org/show_bug.cgi?id=4476 $ hg init foo $ cd foo
--- a/tests/test-journal.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-journal.t Tue Oct 18 14:15:15 2016 -0500 @@ -130,7 +130,7 @@ cb9a9f314b8b bar book -f bar 1e6c11564562 bar book -r tip bar -Test that verbose, JSON and commit output work +Test that verbose, JSON, template and commit output work $ hg journal --verbose --all previous locations of the working copy and bookmarks: @@ -146,37 +146,57 @@ [ { "command": "up", - "date": "1970-01-01 00:00 +0000", + "date": [5.0, 0], "name": ".", - "newhashes": "1e6c11564562", - "oldhashes": "cb9a9f314b8b", + "newhashes": ["1e6c11564562b4ed919baca798bc4338bd299d6a"], + "oldhashes": ["cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b"], "user": "foobar" }, { "command": "up 0", - "date": "1970-01-01 00:00 +0000", + "date": [2.0, 0], "name": ".", - "newhashes": "cb9a9f314b8b", - "oldhashes": "1e6c11564562", + "newhashes": ["cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b"], + "oldhashes": ["1e6c11564562b4ed919baca798bc4338bd299d6a"], "user": "foobar" }, { "command": "commit -Aqm b", - "date": "1970-01-01 00:00 +0000", + "date": [1.0, 0], "name": ".", - "newhashes": "1e6c11564562", - "oldhashes": "cb9a9f314b8b", + "newhashes": ["1e6c11564562b4ed919baca798bc4338bd299d6a"], + "oldhashes": ["cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b"], "user": "foobar" }, { "command": "commit -Aqm a", - "date": "1970-01-01 00:00 +0000", + "date": [0.0, 0], "name": ".", - "newhashes": "cb9a9f314b8b", - "oldhashes": "000000000000", + "newhashes": ["cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b"], + "oldhashes": ["0000000000000000000000000000000000000000"], "user": "foobar" } ] + + $ cat <<EOF >> $HGRCPATH + > [templates] + > j = "{oldhashes % '{node|upper}'} -> {newhashes % '{node|upper}'} + > - user: {user} + > - command: {command} + > - date: {date|rfc3339date} + > - newhashes: {newhashes} + > - oldhashes: {oldhashes} + > " + > EOF + $ hg journal -Tj -l1 + previous locations of '.': + CB9A9F314B8B07BA71012FCDBC544B5A4D82FF5B -> 1E6C11564562B4ED919BACA798BC4338BD299D6A + - user: foobar + - command: up + - date: 1970-01-01T00:00:05+00:00 + - newhashes: 1e6c11564562b4ed919baca798bc4338bd299d6a + - oldhashes: cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b + $ hg journal --commit previous locations of '.': 1e6c11564562 up @@ -212,7 +232,7 @@ Test for behaviour on unexpected storage version information - $ printf '42\0' > .hg/journal + $ printf '42\0' > .hg/namejournal $ hg journal previous locations of '.': abort: unknown journal file version '42'
--- a/tests/test-keyword.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-keyword.t Tue Oct 18 14:15:15 2016 -0500 @@ -1113,11 +1113,11 @@ [1] $ cat m $Id$ - <<<<<<< local: 88a80c8d172e - test: 8bar + <<<<<<< working copy: 88a80c8d172e - test: 8bar bar ======= foo - >>>>>>> other: 85d2d2d732a5 - test: simplemerge + >>>>>>> merge rev: 85d2d2d732a5 - test: simplemerge resolve to local, m must contain hash of last change (local parent)
--- a/tests/test-largefiles-small-disk.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-largefiles-small-disk.t Tue Oct 18 14:15:15 2016 -0500 @@ -11,7 +11,7 @@ > shutil.copyfileobj = copyfileobj > # > # this makes the rewritten code abort: - > def filechunkiter(f, size=65536, limit=None): + > def filechunkiter(f, size=131072, limit=None): > yield f.read(4) > raise IOError(errno.ENOSPC, os.strerror(errno.ENOSPC)) > util.filechunkiter = filechunkiter
--- a/tests/test-largefiles-update.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-largefiles-update.t Tue Oct 18 14:15:15 2016 -0500 @@ -549,7 +549,7 @@ > l > EOF subrepository sub diverged (local revision: f74e50bd9e55, remote revision: d65e59e952a9) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for sub differ (in checked out version) use (l)ocal source (f74e50bd9e55) or (r)emote source (d65e59e952a9)? r remote turned local largefile large2 into a normal file @@ -615,7 +615,7 @@ > EOF rebasing 1:72518492caa6 "#1" rebasing 4:07d6153b5c04 "#4" - local changed .hglf/large1 which remote deleted + local [dest] changed .hglf/large1 which other [source] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? c $ hg diff -c "tip~1" --nodates .hglf/large1 | grep '^[+-][0-9a-z]'
--- a/tests/test-largefiles.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-largefiles.t Tue Oct 18 14:15:15 2016 -0500 @@ -19,10 +19,6 @@ > usercache=${USERCACHE} > [hooks] > precommit=sh -c "echo \\"Invoking status precommit hook\\"; hg status" - > [experimental] - > # drop me once bundle2 is the default, - > # added to get test change early. - > bundle2-exp = True > EOF Create the repo with a couple of revisions of both large and normal @@ -116,7 +112,7 @@ normalnew already tracked! $ hg remove normalnew largenew not removing largenew: file is untracked - not removing normalnew: file has been marked for add (use forget to undo) + not removing normalnew: file has been marked for add (use 'hg forget' to undo add) [1] $ rm normalnew largenew $ hg up -Cq @@ -1104,7 +1100,7 @@ all local heads known remotely 6 changesets found uncompressed size of bundle content: - 1333 (changelog) + 1389 (changelog) 1599 (manifests) 254 .hglf/large1 564 .hglf/large3
--- a/tests/test-lock-badness.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-lock-badness.t Tue Oct 18 14:15:15 2016 -0500 @@ -60,7 +60,7 @@ > > preup 2>&1 $ wait $ cat preup - waiting for lock on working directory of b held by '*:*' (glob) + waiting for lock on working directory of b held by process '*' on host '*' (glob) got lock after * seconds (glob) $ cat stdout adding b
--- a/tests/test-log.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-log.t Tue Oct 18 14:15:15 2016 -0500 @@ -717,6 +717,19 @@ date: Thu Jan 01 00:00:01 1970 +0000 summary: b2 +log -r "follow('set:grep(b2)', 4)" + + $ hg up -qC 0 + $ hg log -r "follow('set:grep(b2)', 4)" + changeset: 4:ddb82e70d1a1 + tag: tip + parent: 0:67e992f2c4f3 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: b2 + + $ hg up -qC 4 + log -f -r null $ hg log -f -r null @@ -920,6 +933,78 @@ $ cd .. +log --follow --patch FILE in repository where linkrev isn't trustworthy +(issue5376) + + $ hg init follow-dup + $ cd follow-dup + $ cat <<EOF >> .hg/hgrc + > [ui] + > logtemplate = '=== {rev}: {desc}\n' + > [diff] + > nodates = True + > EOF + $ echo 0 >> a + $ hg ci -qAm 'a0' + $ echo 1 >> a + $ hg ci -m 'a1' + $ hg up -q 0 + $ echo 1 >> a + $ touch b + $ hg ci -qAm 'a1 with b' + $ echo 3 >> a + $ hg ci -m 'a3' + + fctx.rev() == 2, but fctx.linkrev() == 1 + + $ hg log -pf a + === 3: a3 + diff -r 4ea02ba94d66 -r e7a6331a34f0 a + --- a/a + +++ b/a + @@ -1,2 +1,3 @@ + 0 + 1 + +3 + + === 2: a1 with b + diff -r 49b5e81287e2 -r 4ea02ba94d66 a + --- a/a + +++ b/a + @@ -1,1 +1,2 @@ + 0 + +1 + + === 0: a0 + diff -r 000000000000 -r 49b5e81287e2 a + --- /dev/null + +++ b/a + @@ -0,0 +1,1 @@ + +0 + + + fctx.introrev() == 2, but fctx.linkrev() == 1 + + $ hg up -q 2 + $ hg log -pf a + === 2: a1 with b + diff -r 49b5e81287e2 -r 4ea02ba94d66 a + --- a/a + +++ b/a + @@ -1,1 +1,2 @@ + 0 + +1 + + === 0: a0 + diff -r 000000000000 -r 49b5e81287e2 a + --- /dev/null + +++ b/a + @@ -0,0 +1,1 @@ + +0 + + + $ cd .. + Test that log should respect the order of -rREV even if multiple OR conditions are specified (issue5100): @@ -1662,6 +1747,34 @@ 1:a765632148dc55d38c35c4f247c618701886cb2f 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05 +test hidden revision 0 (issue5385) + + $ hg bookmark -d X@foo + $ hg up null -q + $ hg debugobsolete 9f758d63dcde62d547ebfb08e1e7ee96535f2b05 + $ echo f > b + $ hg ci -Am'b' -d '2 0' + adding b + $ echo f >> b + $ hg ci -m'b bis' -d '3 0' + $ hg log -T'{rev}:{node}\n' + 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e + 2:94375ec45bddd2a824535fc04855bd058c926ec0 + + $ hg log -T'{rev}:{node}\n' -r: + 2:94375ec45bddd2a824535fc04855bd058c926ec0 + 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e + $ hg log -T'{rev}:{node}\n' -r:tip + 2:94375ec45bddd2a824535fc04855bd058c926ec0 + 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e + $ hg log -T'{rev}:{node}\n' -r:0 + abort: hidden revision '0'! + (use --hidden to access hidden revisions) + [255] + $ hg log -T'{rev}:{node}\n' -f + 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e + 2:94375ec45bddd2a824535fc04855bd058c926ec0 + clear extensions configuration $ echo '[extensions]' >> $HGRCPATH $ echo "obs=!" >> $HGRCPATH
--- a/tests/test-lrucachedict.py Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-lrucachedict.py Tue Oct 18 14:15:15 2016 -0500 @@ -25,6 +25,9 @@ d['e'] = 've' printifpresent(d, ['a', 'b', 'c', 'd', 'e']) + assert d.get('a') is None + assert d.get('e') == 've' + # touch entries in some order (get or set). d['e'] d['c'] = 'vc2'
--- a/tests/test-merge-changedelete.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-merge-changedelete.t Tue Oct 18 14:15:15 2016 -0500 @@ -54,9 +54,9 @@ Non-interactive merge: $ hg merge -y - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging file3 warning: conflicts while merging file3! (edit, then use 'hg resolve --mark') @@ -77,6 +77,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -100,11 +103,11 @@ changed --- file3 --- 3 - <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3 + <<<<<<< working copy: 13910f48cf7b - test: changed file1, removed file2, chan... changed2 ======= changed1 - >>>>>>> other: 10f9a0a634e8 - test: removed file1, changed file2, changed file3 + >>>>>>> merge rev: 10f9a0a634e8 - test: removed file1, changed file2, cha... Interactive merge: @@ -117,9 +120,9 @@ > c > d > EOF - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? c - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? d merging file3 warning: conflicts while merging file3! (edit, then use 'hg resolve --mark') @@ -140,6 +143,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -161,11 +167,11 @@ *** file2 does not exist --- file3 --- 3 - <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3 + <<<<<<< working copy: 13910f48cf7b - test: changed file1, removed file2, chan... changed2 ======= changed1 - >>>>>>> other: 10f9a0a634e8 - test: removed file1, changed file2, changed file3 + >>>>>>> merge rev: 10f9a0a634e8 - test: removed file1, changed file2, cha... Interactive merge with bad input: @@ -181,18 +187,18 @@ > baz > c > EOF - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? foo unrecognized response - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? bar unrecognized response - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? d - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? baz unrecognized response - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c merging file3 warning: conflicts while merging file3! (edit, then use 'hg resolve --mark') @@ -213,6 +219,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -234,11 +243,11 @@ changed --- file3 --- 3 - <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3 + <<<<<<< working copy: 13910f48cf7b - test: changed file1, removed file2, chan... changed2 ======= changed1 - >>>>>>> other: 10f9a0a634e8 - test: removed file1, changed file2, changed file3 + >>>>>>> merge rev: 10f9a0a634e8 - test: removed file1, changed file2, cha... Interactive merge with not enough input: @@ -250,9 +259,9 @@ $ hg merge --config ui.interactive=true <<EOF > d > EOF - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? d - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? merging file3 warning: conflicts while merging file3! (edit, then use 'hg resolve --mark') @@ -273,6 +282,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -294,11 +306,11 @@ changed --- file3 --- 3 - <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3 + <<<<<<< working copy: 13910f48cf7b - test: changed file1, removed file2, chan... changed2 ======= changed1 - >>>>>>> other: 10f9a0a634e8 - test: removed file1, changed file2, changed file3 + >>>>>>> merge rev: 10f9a0a634e8 - test: removed file1, changed file2, cha... Choose local versions of files @@ -322,6 +334,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -367,6 +382,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -413,6 +431,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -445,12 +466,12 @@ 1 other heads for branch "default" $ hg merge --config ui.interactive=True --tool :prompt - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? no tool found to merge file3 - keep (l)ocal, take (o)ther, or leave (u)nresolved? + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? 0 files updated, 0 files merged, 0 files removed, 3 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] @@ -467,6 +488,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -501,12 +525,12 @@ 1 other heads for branch "default" $ hg merge --tool :prompt - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u no tool found to merge file3 - keep (l)ocal, take (o)ther, or leave (u)nresolved? u + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? u 0 files updated, 0 files merged, 0 files removed, 3 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] @@ -523,6 +547,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -555,9 +582,9 @@ 1 other heads for branch "default" $ hg merge --tool :merge3 - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging file3 warning: conflicts while merging file3! (edit, then use 'hg resolve --mark') @@ -577,6 +604,9 @@ * version 2 records local: 13910f48cf7bdb2a0ba6e24b4900e4fdd5739dd4 other: 10f9a0a634e82080907e62f075ab119cbc565ea6 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = ab57bf49aa276a22d35a473592d4c34b5abc3eff) file: file1 (record type "C", state "u", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "") @@ -600,12 +630,12 @@ changed --- file3 --- 3 - <<<<<<< local: 13910f48cf7b - test: changed file1, removed file2, changed file3 + <<<<<<< working copy: 13910f48cf7b - test: changed file1, removed file2, chan... changed2 ||||||| base ======= changed1 - >>>>>>> other: 10f9a0a634e8 - test: removed file1, changed file2, changed file3 + >>>>>>> merge rev: 10f9a0a634e8 - test: removed file1, changed file2, cha... Exercise transitions between local, other, fail and prompt, and make sure the dirstate stays consistent. (Compare with each other and to the above @@ -642,12 +672,12 @@ (status identical) === :other -> :prompt === - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? no tool found to merge file3 - keep (l)ocal, take (o)ther, or leave (u)nresolved? + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? --- diff of status --- (status identical) @@ -671,12 +701,12 @@ (status identical) === :local -> :prompt === - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? no tool found to merge file3 - keep (l)ocal, take (o)ther, or leave (u)nresolved? + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? --- diff of status --- (status identical) @@ -690,12 +720,12 @@ (status identical) === :fail -> :prompt === - local changed file1 which remote deleted + local [working copy] changed file1 which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? - remote changed file2 which local deleted + other [merge rev] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? no tool found to merge file3 - keep (l)ocal, take (o)ther, or leave (u)nresolved? + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? --- diff of status --- (status identical) @@ -717,9 +747,9 @@ $ echo changed >> file1 $ hg rm file2 $ hg update 1 -y - local changed file1 which remote deleted + local [working copy] changed file1 which other [destination] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - remote changed file2 which local deleted + other [destination] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u 1 files updated, 0 files merged, 0 files removed, 2 files unresolved use 'hg resolve' to retry unresolved file merges @@ -893,9 +923,9 @@ $ echo changed >> file1 $ hg rm file2 $ hg update 1 --config ui.interactive=True --tool :prompt - local changed file1 which remote deleted + local [working copy] changed file1 which other [destination] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? - remote changed file2 which local deleted + other [destination] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? 1 files updated, 0 files merged, 0 files removed, 2 files unresolved use 'hg resolve' to retry unresolved file merges @@ -943,9 +973,9 @@ $ echo changed >> file1 $ hg rm file2 $ hg update 1 --tool :merge3 - local changed file1 which remote deleted + local [working copy] changed file1 which other [destination] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - remote changed file2 which local deleted + other [destination] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u 1 files updated, 0 files merged, 0 files removed, 2 files unresolved use 'hg resolve' to retry unresolved file merges @@ -999,9 +1029,9 @@ (status identical) === :other -> :prompt === - local changed file1 which remote deleted + local [working copy] changed file1 which other [destination] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? - remote changed file2 which local deleted + other [destination] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? --- diff of status --- (status identical) @@ -1026,9 +1056,9 @@ (status identical) === :local -> :prompt === - local changed file1 which remote deleted + local [working copy] changed file1 which other [destination] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? - remote changed file2 which local deleted + other [destination] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? --- diff of status --- (status identical) @@ -1043,9 +1073,9 @@ (status identical) === :fail -> :prompt === - local changed file1 which remote deleted + local [working copy] changed file1 which other [destination] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? - remote changed file2 which local deleted + other [destination] changed file2 which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? --- diff of status --- (status identical)
--- a/tests/test-merge-force.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-merge-force.t Tue Oct 18 14:15:15 2016 -0500 @@ -142,55 +142,55 @@ # in the same way, so it could potentially be left alone $ hg merge -f --tool internal:merge3 'desc("remote")' 2>&1 | tee $TESTTMP/merge-output-1 - local changed content1_missing_content1_content4-tracked which remote deleted + local [working copy] changed content1_missing_content1_content4-tracked which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - local changed content1_missing_content3_content3-tracked which remote deleted + local [working copy] changed content1_missing_content3_content3-tracked which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - local changed content1_missing_content3_content4-tracked which remote deleted + local [working copy] changed content1_missing_content3_content4-tracked which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - local changed content1_missing_missing_content4-tracked which remote deleted + local [working copy] changed content1_missing_missing_content4-tracked which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - remote changed content1_content2_content1_content1-untracked which local deleted + other [merge rev] changed content1_content2_content1_content1-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content1_content2-untracked which local deleted + other [merge rev] changed content1_content2_content1_content2-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content1_content4-untracked which local deleted + other [merge rev] changed content1_content2_content1_content4-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content1_missing-tracked which local deleted + other [merge rev] changed content1_content2_content1_missing-tracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content1_missing-untracked which local deleted + other [merge rev] changed content1_content2_content1_missing-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content2_content1-untracked which local deleted + other [merge rev] changed content1_content2_content2_content1-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content2_content2-untracked which local deleted + other [merge rev] changed content1_content2_content2_content2-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content2_content4-untracked which local deleted + other [merge rev] changed content1_content2_content2_content4-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content2_missing-tracked which local deleted + other [merge rev] changed content1_content2_content2_missing-tracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content2_missing-untracked which local deleted + other [merge rev] changed content1_content2_content2_missing-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_content1-untracked which local deleted + other [merge rev] changed content1_content2_content3_content1-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_content2-untracked which local deleted + other [merge rev] changed content1_content2_content3_content2-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_content3-untracked which local deleted + other [merge rev] changed content1_content2_content3_content3-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_content4-untracked which local deleted + other [merge rev] changed content1_content2_content3_content4-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_missing-tracked which local deleted + other [merge rev] changed content1_content2_content3_missing-tracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_missing-untracked which local deleted + other [merge rev] changed content1_content2_content3_missing-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_missing_content1-untracked which local deleted + other [merge rev] changed content1_content2_missing_content1-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_missing_content2-untracked which local deleted + other [merge rev] changed content1_content2_missing_content2-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_missing_content4-untracked which local deleted + other [merge rev] changed content1_content2_missing_content4-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_missing_missing-tracked which local deleted + other [merge rev] changed content1_content2_missing_missing-tracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_missing_missing-untracked which local deleted + other [merge rev] changed content1_content2_missing_missing-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_content1_content4-tracked merging content1_content2_content2_content1-tracked @@ -373,13 +373,13 @@ content2 M content1_content2_content1_content4-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content4 ||||||| base content1 ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M content1_content2_content1_content4-untracked content2 @@ -403,13 +403,13 @@ content2 M content1_content2_content2_content4-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content4 ||||||| base content1 ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M content1_content2_content2_content4-untracked content2 @@ -433,25 +433,25 @@ content2 M content1_content2_content3_content3-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content3 ||||||| base content1 ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M content1_content2_content3_content3-untracked content2 M content1_content2_content3_content4-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content4 ||||||| base content1 ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M content1_content2_content3_content4-untracked content2 @@ -475,13 +475,13 @@ content2 M content1_content2_missing_content4-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content4 ||||||| base content1 ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M content1_content2_missing_content4-untracked content2 @@ -559,12 +559,12 @@ content2 M missing_content2_content2_content4-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content4 ||||||| base ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M missing_content2_content2_content4-untracked content2 @@ -582,23 +582,23 @@ content2 M missing_content2_content3_content3-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content3 ||||||| base ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M missing_content2_content3_content3-untracked content2 M missing_content2_content3_content4-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content4 ||||||| base ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M missing_content2_content3_content4-untracked content2 @@ -616,20 +616,20 @@ content2 M missing_content2_missing_content4-tracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content4 ||||||| base ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M missing_content2_missing_content4-untracked - <<<<<<< local: 0447570f1af6 - test: local + <<<<<<< working copy: 0447570f1af6 - test: local content4 ||||||| base ======= content2 - >>>>>>> other: 85100b8c675b - test: remote + >>>>>>> merge rev: 85100b8c675b - test: remote M missing_content2_missing_missing-tracked content2 @@ -703,63 +703,63 @@ (no more unresolved files) $ hg resolve --unmark --all $ hg resolve --all --tool internal:merge3 - remote changed content1_content2_content1_content1-untracked which local deleted + other [merge rev] changed content1_content2_content1_content1-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content1_content2-untracked which local deleted + other [merge rev] changed content1_content2_content1_content2-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_content1_content4-tracked - remote changed content1_content2_content1_content4-untracked which local deleted + other [merge rev] changed content1_content2_content1_content4-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content1_missing-tracked which local deleted + other [merge rev] changed content1_content2_content1_missing-tracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content1_missing-untracked which local deleted + other [merge rev] changed content1_content2_content1_missing-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_content2_content1-tracked - remote changed content1_content2_content2_content1-untracked which local deleted + other [merge rev] changed content1_content2_content2_content1-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content2_content2-untracked which local deleted + other [merge rev] changed content1_content2_content2_content2-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_content2_content4-tracked - remote changed content1_content2_content2_content4-untracked which local deleted + other [merge rev] changed content1_content2_content2_content4-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content2_missing-tracked which local deleted + other [merge rev] changed content1_content2_content2_missing-tracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content2_missing-untracked which local deleted + other [merge rev] changed content1_content2_content2_missing-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_content3_content1-tracked - remote changed content1_content2_content3_content1-untracked which local deleted + other [merge rev] changed content1_content2_content3_content1-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_content2-untracked which local deleted + other [merge rev] changed content1_content2_content3_content2-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_content3_content3-tracked - remote changed content1_content2_content3_content3-untracked which local deleted + other [merge rev] changed content1_content2_content3_content3-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_content3_content4-tracked - remote changed content1_content2_content3_content4-untracked which local deleted + other [merge rev] changed content1_content2_content3_content4-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_missing-tracked which local deleted + other [merge rev] changed content1_content2_content3_missing-tracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_content3_missing-untracked which local deleted + other [merge rev] changed content1_content2_content3_missing-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_missing_content1-tracked - remote changed content1_content2_missing_content1-untracked which local deleted + other [merge rev] changed content1_content2_missing_content1-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_missing_content2-untracked which local deleted + other [merge rev] changed content1_content2_missing_content2-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging content1_content2_missing_content4-tracked - remote changed content1_content2_missing_content4-untracked which local deleted + other [merge rev] changed content1_content2_missing_content4-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_missing_missing-tracked which local deleted + other [merge rev] changed content1_content2_missing_missing-tracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - remote changed content1_content2_missing_missing-untracked which local deleted + other [merge rev] changed content1_content2_missing_missing-untracked which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u - local changed content1_missing_content1_content4-tracked which remote deleted + local [working copy] changed content1_missing_content1_content4-tracked which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - local changed content1_missing_content3_content3-tracked which remote deleted + local [working copy] changed content1_missing_content3_content3-tracked which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - local changed content1_missing_content3_content4-tracked which remote deleted + local [working copy] changed content1_missing_content3_content4-tracked which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u - local changed content1_missing_missing_content4-tracked which remote deleted + local [working copy] changed content1_missing_missing_content4-tracked which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u merging missing_content2_content2_content4-tracked merging missing_content2_content3_content3-tracked
--- a/tests/test-merge-local.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-merge-local.t Tue Oct 18 14:15:15 2016 -0500 @@ -66,7 +66,7 @@ merging zzz1_merge_ok merging zzz2_merge_bad warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark') - 2 files updated, 1 files merged, 3 files removed, 1 files unresolved + 2 files updated, 1 files merged, 2 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges [1] @@ -104,7 +104,7 @@ merging zzz1_merge_ok merging zzz2_merge_bad warning: conflicts while merging zzz2_merge_bad! (edit, then use 'hg resolve --mark') - 2 files updated, 1 files merged, 3 files removed, 1 files unresolved + 2 files updated, 1 files merged, 2 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges [1]
--- a/tests/test-merge-remove.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-merge-remove.t Tue Oct 18 14:15:15 2016 -0500 @@ -102,7 +102,7 @@ Those who use force will lose $ hg merge -f - remote changed bar which local deleted + other [merge rev] changed bar which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u merging foo1 and foo to foo1 0 files updated, 1 files merged, 0 files removed, 1 files unresolved
--- a/tests/test-merge-tools.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-merge-tools.t Tue Oct 18 14:15:15 2016 -0500 @@ -75,11 +75,11 @@ [1] $ aftermerge # cat f - <<<<<<< local: ef83787e2614 - test: revision 1 + <<<<<<< working copy: ef83787e2614 - test: revision 1 revision 1 ======= revision 2 - >>>>>>> other: 0185f4e0cf02 - test: revision 2 + >>>>>>> merge rev: 0185f4e0cf02 - test: revision 2 space # hg stat M f @@ -532,7 +532,7 @@ # hg update -C 1 $ hg merge -r 2 --config ui.merge=internal:prompt no tool found to merge f - keep (l)ocal, take (o)ther, or leave (u)nresolved? u + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? u 0 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] @@ -557,7 +557,7 @@ > u > EOF no tool found to merge f - keep (l)ocal, take (o)ther, or leave (u)nresolved? u + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? u 0 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] @@ -580,7 +580,7 @@ # hg update -C 1 $ hg merge -r 2 --config ui.merge=internal:prompt --config ui.interactive=true no tool found to merge f - keep (l)ocal, take (o)ther, or leave (u)nresolved? + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? 0 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] @@ -594,7 +594,7 @@ U f $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true no tool found to merge f - keep (l)ocal, take (o)ther, or leave (u)nresolved? + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? [1] $ aftermerge # cat f @@ -608,7 +608,7 @@ $ rm f $ hg resolve --all --config ui.merge=internal:prompt --config ui.interactive=true no tool found to merge f - keep (l)ocal, take (o)ther, or leave (u)nresolved? + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? [1] $ aftermerge # cat f @@ -620,7 +620,7 @@ U f $ hg resolve --all --config ui.merge=internal:prompt no tool found to merge f - keep (l)ocal, take (o)ther, or leave (u)nresolved? u + keep (l)ocal [working copy], take (o)ther [merge rev], or leave (u)nresolved? u [1] $ aftermerge # cat f @@ -935,12 +935,12 @@ # hg update -C 1 $ hg merge -r 4 --config merge-tools.true.premerge=keep merging f - <<<<<<< local: ef83787e2614 - test: revision 1 + <<<<<<< working copy: ef83787e2614 - test: revision 1 revision 1 space ======= revision 4 - >>>>>>> other: 81448d39c9a0 - test: revision 4 + >>>>>>> merge rev: 81448d39c9a0 - test: revision 4 revision 0 space revision 4 @@ -948,12 +948,12 @@ (branch merge, don't forget to commit) $ aftermerge # cat f - <<<<<<< local: ef83787e2614 - test: revision 1 + <<<<<<< working copy: ef83787e2614 - test: revision 1 revision 1 space ======= revision 4 - >>>>>>> other: 81448d39c9a0 - test: revision 4 + >>>>>>> merge rev: 81448d39c9a0 - test: revision 4 # hg stat M f # hg resolve --list @@ -969,7 +969,7 @@ # hg update -C 1 $ hg merge -r 4 --config merge-tools.true.premerge=keep-merge3 merging f - <<<<<<< local: ef83787e2614 - test: revision 1 + <<<<<<< working copy: ef83787e2614 - test: revision 1 revision 1 space ||||||| base @@ -977,7 +977,7 @@ space ======= revision 4 - >>>>>>> other: 81448d39c9a0 - test: revision 4 + >>>>>>> merge rev: 81448d39c9a0 - test: revision 4 revision 0 space revision 4 @@ -985,7 +985,7 @@ (branch merge, don't forget to commit) $ aftermerge # cat f - <<<<<<< local: ef83787e2614 - test: revision 1 + <<<<<<< working copy: ef83787e2614 - test: revision 1 revision 1 space ||||||| base @@ -993,7 +993,7 @@ space ======= revision 4 - >>>>>>> other: 81448d39c9a0 - test: revision 4 + >>>>>>> merge rev: 81448d39c9a0 - test: revision 4 # hg stat M f # hg resolve --list
--- a/tests/test-merge-types.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-merge-types.t Tue Oct 18 14:15:15 2016 -0500 @@ -173,7 +173,7 @@ (couldn't find merge tool hgmerge|tool hgmerge can't handle symlinks) (re) picked tool ':prompt' for a (binary False symlink True changedelete False) no tool found to merge a - keep (l)ocal, take (o)ther, or leave (u)nresolved? u + keep (l)ocal [working copy], take (o)ther [destination], or leave (u)nresolved? u 0 files updated, 0 files merged, 0 files removed, 1 files unresolved use 'hg resolve' to retry unresolved file merges 1 other heads for branch "default" @@ -307,6 +307,8 @@ $ echo 1 > a $ echo 1 > b $ chmod +x b + $ echo 1 > bx + $ chmod +x bx $ echo x > c $ chmod +x c $ echo 1 > d @@ -321,6 +323,8 @@ $ hg up -qr0 $ echo 2 > a $ echo 2 > b + $ echo 2 > bx + $ chmod +x bx $ echo x > c $ ln -s 2 d $ ln -s x e @@ -331,9 +335,10 @@ $ hg merge merging a - warning: cannot merge flags for b + warning: cannot merge flags for b without common ancestor - keeping local flags merging b - warning: cannot merge flags for c + merging bx + warning: cannot merge flags for c without common ancestor - keeping local flags merging d warning: internal :merge cannot merge symlinks for d warning: conflicts while merging d! (edit, then use 'hg resolve --mark') @@ -345,29 +350,31 @@ warning: conflicts while merging h! (edit, then use 'hg resolve --mark') warning: conflicts while merging a! (edit, then use 'hg resolve --mark') warning: conflicts while merging b! (edit, then use 'hg resolve --mark') - 3 files updated, 0 files merged, 0 files removed, 5 files unresolved + warning: conflicts while merging bx! (edit, then use 'hg resolve --mark') + 3 files updated, 0 files merged, 0 files removed, 6 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] $ hg resolve -l U a U b + U bx U d U f U h $ tellmeabout a a is a plain file with content: - <<<<<<< local: 0139c5610547 - test: 2 + <<<<<<< working copy: 0c617753b41b - test: 2 2 ======= 1 - >>>>>>> other: 97e29675e796 - test: 1 + >>>>>>> merge rev: 2e60aa20b912 - test: 1 $ tellmeabout b b is a plain file with content: - <<<<<<< local: 0139c5610547 - test: 2 + <<<<<<< working copy: 0c617753b41b - test: 2 2 ======= 1 - >>>>>>> other: 97e29675e796 - test: 1 + >>>>>>> merge rev: 2e60aa20b912 - test: 1 $ tellmeabout c c is a plain file with content: x @@ -390,9 +397,10 @@ $ hg up -Cqr1 $ hg merge merging a - warning: cannot merge flags for b + warning: cannot merge flags for b without common ancestor - keeping local flags merging b - warning: cannot merge flags for c + merging bx + warning: cannot merge flags for c without common ancestor - keeping local flags merging d warning: internal :merge cannot merge symlinks for d warning: conflicts while merging d! (edit, then use 'hg resolve --mark') @@ -404,23 +412,24 @@ warning: conflicts while merging h! (edit, then use 'hg resolve --mark') warning: conflicts while merging a! (edit, then use 'hg resolve --mark') warning: conflicts while merging b! (edit, then use 'hg resolve --mark') - 3 files updated, 0 files merged, 0 files removed, 5 files unresolved + warning: conflicts while merging bx! (edit, then use 'hg resolve --mark') + 3 files updated, 0 files merged, 0 files removed, 6 files unresolved use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon [1] $ tellmeabout a a is a plain file with content: - <<<<<<< local: 97e29675e796 - test: 1 + <<<<<<< working copy: 2e60aa20b912 - test: 1 1 ======= 2 - >>>>>>> other: 0139c5610547 - test: 2 + >>>>>>> merge rev: 0c617753b41b - test: 2 $ tellmeabout b b is an executable file with content: - <<<<<<< local: 97e29675e796 - test: 1 + <<<<<<< working copy: 2e60aa20b912 - test: 1 1 ======= 2 - >>>>>>> other: 0139c5610547 - test: 2 + >>>>>>> merge rev: 0c617753b41b - test: 2 $ tellmeabout c c is an executable file with content: x
--- a/tests/test-merge7.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-merge7.t Tue Oct 18 14:15:15 2016 -0500 @@ -99,11 +99,11 @@ $ cat test.txt one - <<<<<<< local: 50c3a7e29886 - test: Merge 1 + <<<<<<< working copy: 50c3a7e29886 - test: Merge 1 two-point-five ======= two-point-one - >>>>>>> other: 40d11a4173a8 - test: two -> two-point-one + >>>>>>> merge rev: 40d11a4173a8 - test: two -> two-point-one three $ hg debugindex test.txt
--- a/tests/test-mq-missingfiles.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-mq-missingfiles.t Tue Oct 18 14:15:15 2016 -0500 @@ -42,6 +42,7 @@ $ hg qpush applying changeb unable to find 'b' for patching + (use '--prefix' to apply patch relative to the current directory) 2 out of 2 hunks FAILED -- saving rejects to file b.rej patch failed, unable to continue (try -v) patch failed, rejects left in working directory @@ -147,6 +148,7 @@ $ hg qpush applying changeb unable to find 'b' for patching + (use '--prefix' to apply patch relative to the current directory) 1 out of 1 hunks FAILED -- saving rejects to file b.rej patch failed, unable to continue (try -v) patch failed, rejects left in working directory
--- a/tests/test-mq-qimport-fail-cleanup.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-mq-qimport-fail-cleanup.t Tue Oct 18 14:15:15 2016 -0500 @@ -36,7 +36,7 @@ $ hg pull -q -r 0 . # update phase $ hg qimport -r 0 abort: revision 0 is not mutable - (see "hg help phases" for details) + (see 'hg help phases' for details) [255] $ cd ..
--- a/tests/test-mq-qimport.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-mq-qimport.t Tue Oct 18 14:15:15 2016 -0500 @@ -40,7 +40,7 @@ $ hg qimport -r null abort: revision -1 is not mutable - (see "hg help phases" for details) + (see 'hg help phases' for details) [255] $ hg qseries
--- a/tests/test-mq-safety.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-mq-safety.t Tue Oct 18 14:15:15 2016 -0500 @@ -26,17 +26,17 @@ $ echo babar >> foo $ hg qref abort: cannot qrefresh public revision - (see "hg help phases" for details) + (see 'hg help phases' for details) [255] $ hg revert -a reverting foo $ hg qpop abort: popping would remove a public revision - (see "hg help phases" for details) + (see 'hg help phases' for details) [255] $ hg qfold bar abort: cannot qrefresh public revision - (see "hg help phases" for details) + (see 'hg help phases' for details) [255] $ hg revert -a reverting foo
--- a/tests/test-mq-subrepo.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-mq-subrepo.t Tue Oct 18 14:15:15 2016 -0500 @@ -304,6 +304,7 @@ record this change to '.hgsub'? [Ynesfdaq?] y warning: subrepo spec file '.hgsub' not found + warning: subrepo spec file '.hgsub' not found abort: uncommitted changes in subrepository 'sub' [255] % update substate when adding .hgsub w/clean updated subrepo @@ -319,6 +320,7 @@ record this change to '.hgsub'? [Ynesfdaq?] y warning: subrepo spec file '.hgsub' not found + warning: subrepo spec file '.hgsub' not found path sub source sub revision b2fdb12cd82b021c3b7053d67802e77b6eeaee31
--- a/tests/test-mq.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-mq.t Tue Oct 18 14:15:15 2016 -0500 @@ -96,7 +96,7 @@ qtop print the name of the current patch qunapplied print the patches not yet applied - (use "hg help -v mq" to show built-in aliases and global options) + (use 'hg help -v mq' to show built-in aliases and global options) $ hg init a $ cd a
--- a/tests/test-obsolete-changeset-exchange.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-obsolete-changeset-exchange.t Tue Oct 18 14:15:15 2016 -0500 @@ -144,11 +144,11 @@ adding file changes adding foo revisions added 1 changesets with 1 changes to 1 files (+1 heads) - bundle2-input-part: total payload size 474 + updating the branch cache + bundle2-input-part: total payload size 476 bundle2-input-part: "listkeys" (params: 1 mandatory) supported bundle2-input-part: total payload size 58 bundle2-input-part: "listkeys" (params: 1 mandatory) supported bundle2-input-bundle: 2 parts total checking for updated bookmarks - updating the branch cache (run 'hg heads' to see heads, 'hg merge' to merge)
--- a/tests/test-obsolete-checkheads.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-obsolete-checkheads.t Tue Oct 18 14:15:15 2016 -0500 @@ -93,7 +93,7 @@ pushing to $TESTTMP/remote (glob) searching for changes abort: push creates new remote head 71e3228bffe1! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] old head is now public (public remote version) @@ -122,7 +122,7 @@ # pushing to $TESTTMP/remote # searching for changes # abort: push creates new remote head 71e3228bffe1! -# (merge or see "hg help push" for details about pushing new heads) +# (merge or see 'hg help push' for details about pushing new heads) # [255] old head is obsolete but replacement is not pushed @@ -153,7 +153,7 @@ pushing to $TESTTMP/remote (glob) searching for changes abort: push creates new remote head d7d41ccbd4de! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255]
--- a/tests/test-obsolete.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-obsolete.t Tue Oct 18 14:15:15 2016 -0500 @@ -4,10 +4,6 @@ > publish=false > [ui] > logtemplate="{rev}:{node|short} ({phase}) [{tags} {bookmarks}] {desc|firstline}\n" - > [experimental] - > # drop me once bundle2 is the default, - > # added to get test change early. - > bundle2-exp = True > EOF $ mkcommit() { > echo "$1" > "$1" @@ -649,12 +645,86 @@ cda648ca50f50482b7055c0b0c4c117bba6733d9 3de5eca88c00aa039da7399a220f4a5221faa585 0 (*) {'user': 'test'} (glob) cdbce2fbb16313928851e97e0d85413f3f7eb77f ca819180edb99ed25ceafb3e9584ac287e240b00 0 (Thu Jan 01 00:22:17 1970 +0000) {'user': 'test'} +List of all markers in JSON + + $ hg debugobsolete -Tjson + [ + { + "date": [1339.0, 0], + "flag": 0, + "metadata": {"user": "test"}, + "precnode": "1339133913391339133913391339133913391339", + "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"] + }, + { + "date": [1339.0, 0], + "flag": 0, + "metadata": {"user": "test"}, + "precnode": "1337133713371337133713371337133713371337", + "succnodes": ["5601fb93a350734d935195fee37f4054c529ff39"] + }, + { + "date": [121.0, 120], + "flag": 12, + "metadata": {"user": "test"}, + "precnode": "245bde4270cd1072a27757984f9cda8ba26f08ca", + "succnodes": ["cdbce2fbb16313928851e97e0d85413f3f7eb77f"] + }, + { + "date": [1338.0, 0], + "flag": 1, + "metadata": {"user": "test"}, + "precnode": "5601fb93a350734d935195fee37f4054c529ff39", + "succnodes": ["6f96419950729f3671185b847352890f074f7557"] + }, + { + "date": [1338.0, 0], + "flag": 0, + "metadata": {"user": "test"}, + "precnode": "ca819180edb99ed25ceafb3e9584ac287e240b00", + "succnodes": ["1337133713371337133713371337133713371337"] + }, + { + "date": [1337.0, 0], + "flag": 0, + "metadata": {"user": "test"}, + "precnode": "cdbce2fbb16313928851e97e0d85413f3f7eb77f", + "succnodes": ["ca819180edb99ed25ceafb3e9584ac287e240b00"] + }, + { + "date": [0.0, 0], + "flag": 0, + "metadata": {"user": "test"}, + "parentnodes": ["6f96419950729f3671185b847352890f074f7557"], + "precnode": "94b33453f93bdb8d457ef9b770851a618bf413e1", + "succnodes": [] + }, + { + "date": *, (glob) + "flag": 0, + "metadata": {"user": "test"}, + "precnode": "cda648ca50f50482b7055c0b0c4c117bba6733d9", + "succnodes": ["3de5eca88c00aa039da7399a220f4a5221faa585"] + } + ] + +Template keywords + + $ hg debugobsolete -r6 -T '{succnodes % "{node|short}"} {date|shortdate}\n' + 3de5eca88c00 ????-??-?? (glob) + $ hg debugobsolete -r6 -T '{join(metadata % "{key}={value}", " ")}\n' + user=test + $ hg debugobsolete -r6 -T '{metadata}\n' + 'user': 'test' + $ hg debugobsolete -r6 -T '{flag} {get(metadata, "user")}\n' + 0 test + #if serve Test the debug output for exchange ---------------------------------- - $ hg pull ../tmpb --config 'experimental.obsmarkers-exchange-debug=True' --config 'experimental.bundle2-exp=True' + $ hg pull ../tmpb --config 'experimental.obsmarkers-exchange-debug=True' # bundle2 pulling from ../tmpb searching for changes no changes found @@ -1114,6 +1184,25 @@ $ hg debugobsolete --index --rev "3+7" 1 6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1 d27fb9b066076fd921277a4b9e8b9cb48c95bc6a 0 \(.*\) {'user': 'test'} (re) 3 4715cf767440ed891755448016c2b8cf70760c30 7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d 0 \(.*\) {'user': 'test'} (re) + $ hg debugobsolete --index --rev "3+7" -Tjson + [ + { + "date": *, (glob) + "flag": 0, + "index": 1, + "metadata": {"user": "test"}, + "precnode": "6fdef60fcbabbd3d50e9b9cbc2a240724b91a5e1", + "succnodes": ["d27fb9b066076fd921277a4b9e8b9cb48c95bc6a"] + }, + { + "date": *, (glob) + "flag": 0, + "index": 3, + "metadata": {"user": "test"}, + "precnode": "4715cf767440ed891755448016c2b8cf70760c30", + "succnodes": ["7ae79c5d60f049c7b0dd02f5f25b9d60aaf7b36d"] + } + ] Test the --delete option of debugobsolete command $ hg debugobsolete --index
--- a/tests/test-parse-date.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-parse-date.t Tue Oct 18 14:15:15 2016 -0500 @@ -102,7 +102,7 @@ Negative range $ hg log -d "--2" - abort: -2 must be nonnegative (see "hg help dates") + abort: -2 must be nonnegative (see 'hg help dates') [255] Whitespace only
--- a/tests/test-patchbomb.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-patchbomb.t Tue Oct 18 14:15:15 2016 -0500 @@ -352,14 +352,14 @@ Content-Disposition: attachment; filename="bundle.hg" Content-Transfer-Encoding: base64 - SEcyMAAAAA5Db21wcmVzc2lvbj1CWkJaaDkxQVkmU1kIqE7KAAAKf//7vFYQWD1/4H7R09C/470I - Ak0E4peoSIYIgQCgGUQOcLABGY2hqoTTCYaBqaYAAACaaMAATIwAA1MIYDaQaqn6p+jRop+oJkA2 - oNqD1PU0PUBoxqaMmjMUepoBoDT1GmQNBKmlTT1GTCNMEAYTQ0NNDI0BoMQHpAZAA8o2pkyNJHfX - RRbXoyxKRUlAg41B3lpmMOnr77dEpFKAvEUGEkWuC4wioiMjC2Y2a84EXhsNCFIrbXUGId07PJnS - ELAOIpL/gE8R8CUeXuw2NKMtkFoLPkcTSomXtgHSg1IKaCNlWwVU3CpmMYqh5gkFYJKOD4UhVVQ6 - SiF1DpE8ghWvF1ih+fYgagfYHI96w/QsrRATpYiP7VRbINFrQy2c21mZ7M4pXXrPBypoXAIhtum7 - aKDJCpUqMDF5dfiDChMfgH9nQ4B60Uvgb4AK9dsbSYc+O3tEyNq9g9gZeA5Je2T82GzjC4DbY4F2 - 0kdrTBwslErFshCgDzeEBwICg13oQaQawQA1WWd3F3JFOFCQCKhOyg== + SEcyMAAAAA5Db21wcmVzc2lvbj1CWkJaaDkxQVkmU1nYvy2xAAAJf//7vFYQXD1/4H7R09C/470I + Ak0E4pe4SIIIgQSgGEQOcLAA5VBKqeppMxTI0YjQNBgQMQDI0GgMhtR6I0GI2p6I0yeSEVT9MiYn + qjCYQwCBtARptARgBNDEwAGiDCMA40NGjQaNA0AAAAADIAAAA0BkABktCk6qObVxZ2A/33KHibLr + UQ4BwkgcPcmuCUAQZCztIWgR1SpBS6IqbIij4UFwhnnMkElcFTqoucIWbsBPK3l+6c+xYaVBWsJo + aT0OV/YAOvLrziifDQMJOMIaaYce9agtI2EwQBAq089UiRU+evFHSLRBT7Wa/D/YBaUtU5ezvtr3 + 6yrIS4Iyp9VWESdWPEi6VjRjdcEY4HvbmDIVEAEVJIUrHNIBx/MmnBBRkw8tSlCQ8ABZxf5ejgBI + pP5TSQPLVMYbq1qbBPmWN0LYVlAvRbP4X512kDQZ9y4TQbvoZmhe+54sRsEJ8GW3hMJjERh0NNlg + aB+3Cw/4u5IpwoSGxfltiA== --===============*==-- (glob) with a specific bundle type @@ -632,7 +632,7 @@ $ hg commit -A -d '5 0' -m 'isolatin 8-bit encoding' adding isolatin -fake ascii mbox: +iso-8859-1 mbox: $ hg email --date '1970-1-1 0:5' -f quux -t foo -c bar -r tip -m mbox this patch series consists of 1 patches. @@ -640,9 +640,9 @@ sending [PATCH] isolatin 8-bit encoding ... $ cat mbox From quux ... ... .. ..:..:.. .... (re) - Content-Type: text/plain; charset="us-ascii" + Content-Type: text/plain; charset="iso-8859-1" MIME-Version: 1.0 - Content-Transfer-Encoding: 8bit + Content-Transfer-Encoding: quoted-printable Subject: [PATCH] isolatin 8-bit encoding X-Mercurial-Node: 240fb913fc1b7ff15ddb9f33e73d82bf5277c720 X-Mercurial-Series-Index: 1 @@ -667,7 +667,7 @@ --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/isolatin Thu Jan 01 00:00:05 1970 +0000 @@ -0,0 +1,1 @@ - +h\xf6mma! (esc) + +h=F6mma!
--- a/tests/test-phases-exchange.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-phases-exchange.t Tue Oct 18 14:15:15 2016 -0500 @@ -1,12 +1,5 @@ #require killdaemons - $ cat << EOF >> $HGRCPATH - > [experimental] - > # drop me once bundle2 is the default, - > # added to get test change early. - > bundle2-exp = True - > EOF - $ hgph() { hg log -G --template "{rev} {phase} {desc} - {node|short}\n" $*; } $ mkcommit() { @@ -772,7 +765,7 @@ searching for changes 1 changesets found uncompressed size of bundle content: - 192 (changelog) + 178 (changelog) 165 (manifests) 131 a-H adding changesets @@ -927,7 +920,7 @@ pushing to ../mu searching for changes abort: push creates new remote head 435b5d83910c! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg push -fr 435b5d83910c ../mu # because the push will create new visible head pushing to ../mu @@ -1048,7 +1041,7 @@ $ cat ../beta.pid >> $DAEMON_PIDS $ cd ../gamma - $ hg pull http://localhost:$HGPORT/ --config experimental.bundle2-exp=True + $ hg pull http://localhost:$HGPORT/ # bundle2+ pulling from http://localhost:$HGPORT/ searching for changes no changes found @@ -1057,7 +1050,7 @@ enforce bundle1 - $ hg pull http://localhost:$HGPORT/ --config experimental.bundle2-exp=False + $ hg pull http://localhost:$HGPORT/ --config devel.legacy.exchange=bundle1 pulling from http://localhost:$HGPORT/ searching for changes no changes found @@ -1189,10 +1182,40 @@ cannot lock source repo, skipping local public phase update [1] $ chmod -R +w .hg - $ hgph Upsilon $ cd .. - $ killdaemons.py +#endif + +Test that clone behaves like pull and doesn't +publish changesets as plain push does + + $ hg -R Upsilon phase -q --force --draft 2 + $ hg clone -q Upsilon Pi -r 7 + $ hgph Upsilon -r 'min(draft())' + o 2 draft a-C - 54acac6f23ab + | + ~ -#endif + $ hg -R Upsilon push Pi -r 7 + pushing to Pi + searching for changes + no changes found + [1] + $ hgph Upsilon -r 'min(draft())' + o 8 draft a-F - b740e3e5c05d + | + ~ + + $ hg -R Upsilon push Pi -r 8 + pushing to Pi + searching for changes + adding changesets + adding manifests + adding file changes + added 1 changesets with 1 changes to 1 files + + $ hgph Upsilon -r 'min(draft())' + o 9 draft a-G - 3e27b6f1eee1 + | + ~
--- a/tests/test-profile.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-profile.t Tue Oct 18 14:15:15 2016 -0500 @@ -31,4 +31,18 @@ #endif +#if lsprof serve + +Profiling of HTTP requests works + + $ hg --profile --config profiling.format=text --config profiling.output=../profile.log serve -d -p $HGPORT --pid-file ../hg.pid -A ../access.log + $ cat ../hg.pid >> $DAEMON_PIDS + $ hg -q clone -U http://localhost:$HGPORT ../clone + +A single profile is logged because file logging doesn't append + $ grep CallCount ../profile.log | wc -l + \s*1 (re) + +#endif + $ cd ..
--- a/tests/test-pull-http.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-pull-http.t Tue Oct 18 14:15:15 2016 -0500 @@ -26,12 +26,12 @@ updating to branch default 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ cat test3/.hg/hgrc - # example repository config (see "hg help config" for more info) + # example repository config (see 'hg help config' for more info) [paths] default = http://foo@localhost:$HGPORT/ # path aliases to other clones of this repo in URLs or filesystem paths - # (see "hg help config.paths" for more info) + # (see 'hg help config.paths' for more info) # # default-push = ssh://jdoe@example.net/hg/jdoes-fork # my-fork = ssh://jdoe@example.net/hg/jdoes-fork @@ -48,11 +48,11 @@ $ echo 'allowpull = false' >> .hg/hgrc $ hg serve -p $HGPORT -d --pid-file=hg.pid -E errors.log $ cat hg.pid >> $DAEMON_PIDS - $ hg clone http://localhost:$HGPORT/ test4 --config experimental.bundle2-exp=True + $ hg clone http://localhost:$HGPORT/ test4 # bundle2+ requesting all changes abort: authorization failed [255] - $ hg clone http://localhost:$HGPORT/ test4 --config experimental.bundle2-exp=False + $ hg clone http://localhost:$HGPORT/ test4 --config devel.legacy.exchange=bundle1 abort: authorization failed [255] $ killdaemons.py
--- a/tests/test-push-hook-lock.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-push-hook-lock.t Tue Oct 18 14:15:15 2016 -0500 @@ -26,7 +26,7 @@ $ echo bar >> 3/foo $ hg --cwd 3 ci -m bar - $ hg --cwd 3 push ../2 --config experimental.bundle2-exp=False + $ hg --cwd 3 push ../2 --config devel.legacy.exchange=bundle1 pushing to ../2 searching for changes adding changesets @@ -38,7 +38,7 @@ $ hg --cwd 1 --config extensions.strip= strip tip -q $ hg --cwd 2 --config extensions.strip= strip tip -q - $ hg --cwd 3 push ../2 --config experimental.bundle2-exp=True + $ hg --cwd 3 push ../2 # bundle2+ pushing to ../2 searching for changes adding changesets
--- a/tests/test-push-http-bundle1.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-push-http-bundle1.t Tue Oct 18 14:15:15 2016 -0500 @@ -5,9 +5,9 @@ which does not need to exist to keep bundle1 working. $ cat << EOF >> $HGRCPATH - > [experimental] + > [devel] > # This test is dedicated to interaction through old bundle - > bundle2-exp = False + > legacy.exchange = bundle1 > EOF $ hg init test
--- a/tests/test-push-warn.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-push-warn.t Tue Oct 18 14:15:15 2016 -0500 @@ -1,9 +1,3 @@ - $ cat << EOF >> $HGRCPATH - > [experimental] - > # drop me once bundle2 is the default, - > # added to get test change early. - > bundle2-exp = True - > EOF $ hg init a $ cd a $ echo foo > t1 @@ -38,7 +32,7 @@ searching for changes remote has heads on branch 'default' that are not known locally: 1c9246a22a0a abort: push creates new remote head 1e108cc5548c! - (pull and merge or see "hg help push" for details about pushing new heads) + (pull and merge or see 'hg help push' for details about pushing new heads) [255] $ hg push --debug ../a @@ -56,7 +50,7 @@ new remote heads on branch 'default': 1e108cc5548c abort: push creates new remote head 1e108cc5548c! - (pull and merge or see "hg help push" for details about pushing new heads) + (pull and merge or see 'hg help push' for details about pushing new heads) [255] $ hg pull ../a @@ -72,7 +66,7 @@ pushing to ../a searching for changes abort: push creates new remote head 1e108cc5548c! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg merge @@ -125,7 +119,7 @@ pushing to ../c searching for changes abort: push creates new remote head 6346d66eb9f5! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg push -r 2 ../c @@ -138,7 +132,7 @@ pushing to ../c searching for changes abort: push creates new remote head a5dda829a167! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg push -v -r 3 -r 4 ../c @@ -148,7 +142,7 @@ a5dda829a167 ee8fbc7a0295 abort: push creates new remote head a5dda829a167! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg push -v -f -r 3 -r 4 ../c @@ -156,7 +150,7 @@ searching for changes 2 changesets found uncompressed size of bundle content: - 348 (changelog) + 352 (changelog) 326 (manifests) 253 foo adding changesets @@ -282,7 +276,7 @@ pushing to ../f searching for changes abort: push creates new remote head 0b715ef6ff8f on branch 'a'! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] Push replacement head on existing branches: @@ -387,7 +381,7 @@ pushing to ../f searching for changes abort: push creates new branch 'f' with multiple heads - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg push --branch f --new-branch --force ../f pushing to ../f @@ -431,7 +425,7 @@ searching for changes remote has heads on branch 'default' that are not known locally: 534543e22c29 764f8ec07b96 afe7cc7679f5 ce4212fc8847 abort: push creates new remote head 97bd0c84d346! - (pull and merge or see "hg help push" for details about pushing new heads) + (pull and merge or see 'hg help push' for details about pushing new heads) [255] $ hg -R h up -q 0; echo x > h/b; hg -R h ci -qAmx $ hg -R i push h @@ -439,7 +433,7 @@ searching for changes remote has heads on branch 'default' that are not known locally: 18ddb72c4590 534543e22c29 764f8ec07b96 afe7cc7679f5 and 1 others abort: push creates new remote head 97bd0c84d346! - (pull and merge or see "hg help push" for details about pushing new heads) + (pull and merge or see 'hg help push' for details about pushing new heads) [255] $ hg -R i push h -v pushing to h @@ -448,7 +442,7 @@ new remote heads on branch 'default': 97bd0c84d346 abort: push creates new remote head 97bd0c84d346! - (pull and merge or see "hg help push" for details about pushing new heads) + (pull and merge or see 'hg help push' for details about pushing new heads) [255] @@ -519,7 +513,7 @@ pushing to ../l searching for changes abort: push creates new remote head 451211cc22b0 on branch 'a'! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ cd .. @@ -769,14 +763,14 @@ pushing to inner searching for changes abort: push creates new remote head 7d0f4fb6cf04 on branch 'A'! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg push inner -r4 -r5 pushing to inner searching for changes abort: push creates new remote head 7d0f4fb6cf04 on branch 'A'! - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg in inner @@ -792,7 +786,7 @@ 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) + (merge or see 'hg help push' for details about pushing new heads) [255] $ cd ..
--- a/tests/test-qrecord.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-qrecord.t Tue Oct 18 14:15:15 2016 -0500 @@ -9,7 +9,7 @@ record extension - commands to interactively select changes for commit/qrefresh (DEPRECATED) - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) help qrecord (no record) @@ -19,7 +19,7 @@ record commands to interactively select changes for commit/qrefresh (DEPRECATED) - (use "hg help extensions" for information on enabling extensions) + (use 'hg help extensions' for information on enabling extensions) $ echo "[extensions]" >> $HGRCPATH $ echo "record=" >> $HGRCPATH @@ -55,7 +55,7 @@ This command is not available when committing a merge. - (use "hg help -e record" to show help for the record extension) + (use 'hg help -e record' to show help for the record extension) options ([+] can be repeated): @@ -99,7 +99,7 @@ interactively record a new patch - (use "hg qrecord -h" to show more help) + (use 'hg qrecord -h' to show more help) [255] qrecord patch (mq not present)
--- a/tests/test-rebase-conflicts.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rebase-conflicts.t Tue Oct 18 14:15:15 2016 -0500 @@ -75,7 +75,7 @@ $ hg rebase --continue already rebased 3:3163e20567cc "L1" as 3e046f2ecedb rebasing 4:46f0b057b5c0 "L2" - abort: unresolved merge conflicts (see "hg help resolve") + abort: unresolved merge conflicts (see 'hg help resolve') [255] Conclude rebase: @@ -219,7 +219,7 @@ summary: added default.txt $ hg rebase -s9 -d2 --debug # use debug to really check merge base used - rebase onto 2 starting from e31216eec445 + rebase onto 4bc80088dc6b starting from e31216eec445 ignoring null merge rebase of 3 ignoring null merge rebase of 4 ignoring null merge rebase of 6 @@ -238,6 +238,8 @@ merge against 9:e31216eec445 detach base 8:8e4e2c1a07ae searching for copies back to rev 3 + unmatched files in other (from topological common ancestor): + f2.txt resolving manifests branchmerge: True, force: True, partial: False ancestor: 8e4e2c1a07ae, local: 4bc80088dc6b+, remote: e31216eec445 @@ -255,6 +257,8 @@ merge against 10:2f2496ddf49d detach base 9:e31216eec445 searching for copies back to rev 3 + unmatched files in other (from topological common ancestor): + f2.txt resolving manifests branchmerge: True, force: True, partial: False ancestor: e31216eec445, local: 19c888675e13+, remote: 2f2496ddf49d @@ -299,12 +303,11 @@ adding file changes adding f1.txt revisions added 2 changesets with 2 changes to 1 files - bundle2-input-part: total payload size 1713 + bundle2-input-part: total payload size 1686 bundle2-input-bundle: 0 parts total invalid branchheads cache (served): tip differs history modification detected - truncating revision branch cache to revision 9 rebase completed - updating the branch cache truncating cache/rbc-revs-v1 to 72 Test minimization of merge conflicts
--- a/tests/test-rebase-mq-skip.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rebase-mq-skip.t Tue Oct 18 14:15:15 2016 -0500 @@ -70,14 +70,14 @@ $TESTTMP/a/.hg/patches/p0.patch (glob) 2 changesets found uncompressed size of bundle content: - 384 (changelog) + 348 (changelog) 324 (manifests) 129 p0 129 p1 saved backup bundle to $TESTTMP/a/.hg/strip-backup/13a46ce44f60-5da6ecfb-backup.hg (glob) 2 changesets found uncompressed size of bundle content: - 439 (changelog) + 403 (changelog) 324 (manifests) 129 p0 129 p1
--- a/tests/test-rebase-newancestor.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rebase-newancestor.t Tue Oct 18 14:15:15 2016 -0500 @@ -135,7 +135,7 @@ note: rebase of 1:1d1a643d390e created no changes to commit rebasing 2:ec2c14fb2984 "dev: f-dev stuff" rebasing 4:4b019212aaf6 "dev: merge default" - remote changed f-default which local deleted + other [source] changed f-default which local [dest] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c rebasing 6:9455ee510502 "dev: merge default" saved backup bundle to $TESTTMP/ancestor-merge/.hg/strip-backup/1d1a643d390e-43e9e04b-backup.hg (glob) @@ -164,7 +164,7 @@ > EOF rebasing 2:ec2c14fb2984 "dev: f-dev stuff" rebasing 4:4b019212aaf6 "dev: merge default" - remote changed f-default which local deleted + other [source] changed f-default which local [dest] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? c rebasing 6:9455ee510502 "dev: merge default" saved backup bundle to $TESTTMP/ancestor-merge-2/.hg/strip-backup/ec2c14fb2984-62d0b222-backup.hg (glob) @@ -304,13 +304,13 @@ rebase merging completed 1 changesets found uncompressed size of bundle content: - 213 (changelog) + 199 (changelog) 216 (manifests) 182 other saved backup bundle to $TESTTMP/parentorder/.hg/strip-backup/4c5f12f25ebe-f46990e5-backup.hg (glob) 1 changesets found uncompressed size of bundle content: - 272 (changelog) + 254 (changelog) 167 (manifests) 182 other adding branch
--- a/tests/test-rebase-obsolete.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rebase-obsolete.t Tue Oct 18 14:15:15 2016 -0500 @@ -418,16 +418,16 @@ ------------------------------------ $ hg rebase --dest 4 --rev '7+11+9' + rebasing 9:cf44d2f5a9f4 "D" rebasing 7:02de42196ebe "H" - rebasing 9:cf44d2f5a9f4 "D" not rebasing ignored 10:7c6027df6a99 "B" rebasing 11:0d8f238b634c "C" (tip) $ hg log -G o 14:1e8370e38cca C | - | o 13:102b4c1d889b D - | | - @ | 12:bfe264faf697 H + @ 13:bfe264faf697 H + | + | o 12:102b4c1d889b D |/ | o 10:7c6027df6a99 B | |
--- a/tests/test-rebase-scenario-global.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rebase-scenario-global.t Tue Oct 18 14:15:15 2016 -0500 @@ -326,7 +326,7 @@ [1] $ hg rebase -d 5 -b 6 abort: can't rebase public changeset e1c4361dd923 - (see "hg help phases" for details) + (see 'hg help phases' for details) [255] $ hg rebase -d 5 -b 6 --keep @@ -758,9 +758,74 @@ $ hg commit -m 'second source with subdir' $ hg rebase -b . -d 1 --traceback rebasing 2:779a07b1b7a0 "first source commit" + current directory was removed + (consider changing to repo root: $TESTTMP/cwd-vanish) rebasing 3:a7d6f3a00bf3 "second source with subdir" (tip) saved backup bundle to $TESTTMP/cwd-vanish/.hg/strip-backup/779a07b1b7a0-853e0073-backup.hg (glob) +Test that rebase is done in topo order (issue5370) + + $ cd .. + $ hg init order + $ cd order + $ touch a && hg add a && hg ci -m A + $ touch b && hg add b && hg ci -m B + $ touch c && hg add c && hg ci -m C + $ hg up 1 + 0 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ touch d && hg add d && hg ci -m D + created new head + $ hg up 2 + 1 files updated, 0 files merged, 1 files removed, 0 files unresolved + $ touch e && hg add e && hg ci -m E + $ hg up 3 + 1 files updated, 0 files merged, 2 files removed, 0 files unresolved + $ touch f && hg add f && hg ci -m F + $ hg up 0 + 0 files updated, 0 files merged, 3 files removed, 0 files unresolved + $ touch g && hg add g && hg ci -m G + created new head + + $ hg tglog + @ 6: 'G' + | + | o 5: 'F' + | | + | | o 4: 'E' + | | | + | o | 3: 'D' + | | | + | | o 2: 'C' + | |/ + | o 1: 'B' + |/ + o 0: 'A' + + + $ hg rebase -s 1 -d 6 + rebasing 1:76035bbd54bd "B" + rebasing 2:d84f5cfaaf14 "C" + rebasing 4:82ae8dc7a9b7 "E" + rebasing 3:ab709c9f7171 "D" + rebasing 5:412b391de760 "F" + saved backup bundle to $TESTTMP/cwd-vanish/order/.hg/strip-backup/76035bbd54bd-e341bc99-backup.hg (glob) + + $ hg tglog + o 6: 'F' + | + o 5: 'D' + | + | o 4: 'E' + | | + | o 3: 'C' + |/ + o 2: 'B' + | + @ 1: 'G' + | + o 0: 'A' + + Test experimental revset ========================
--- a/tests/test-rebuildstate.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rebuildstate.t Tue Oct 18 14:15:15 2016 -0500 @@ -48,8 +48,8 @@ state dump after $ hg debugstate --nodates | sort - n 644 -1 set bar - n 644 -1 set foo + n 0 -1 unset bar + n 0 -1 unset foo $ hg debugadddrop --normal-lookup file1 file2 $ hg debugadddrop --drop bar @@ -57,7 +57,7 @@ $ hg debugstate --nodates n 0 -1 unset file1 n 0 -1 unset file2 - n 644 -1 set foo + n 0 -1 unset foo $ hg debugrebuildstate status @@ -115,7 +115,7 @@ $ hg debugrebuilddirstate --minimal $ hg debugdirstate --nodates r 0 0 * bar (glob) - n 644 -1 * foo (glob) + n 0 -1 * foo (glob) a 0 -1 * qux (glob) $ hg status -A A qux
--- a/tests/test-record.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-record.t Tue Oct 18 14:15:15 2016 -0500 @@ -41,7 +41,7 @@ This command is not available when committing a merge. - (use "hg help -e record" to show help for the record extension) + (use 'hg help -e record' to show help for the record extension) options ([+] can be repeated): @@ -77,6 +77,7 @@ examine changes to 'empty-rw'? [Ynesfdaq?] n no changes to record + [1] $ hg tip -p changeset: -1:000000000000
--- a/tests/test-remove.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-remove.t Tue Oct 18 14:15:15 2016 -0500 @@ -50,7 +50,7 @@ \r (no-eol) (esc) skipping [===========================================>] 1/1\r (no-eol) (esc) \r (no-eol) (esc) - not removing bar: file has been marked for add (use forget to undo) + not removing bar: file has been marked for add (use 'hg forget' to undo add) exit code: 1 A bar ./bar
--- a/tests/test-rename-dir-merge.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rename-dir-merge.t Tue Oct 18 14:15:15 2016 -0500 @@ -144,11 +144,11 @@ C b/a C b/b $ cat b/c - <<<<<<< local: f1c50ca4f127 - test: new file in target directory + <<<<<<< working copy: f1c50ca4f127 - test: new file in target directory target ======= baz - >>>>>>> other: ce36d17b18fb - test: 2 add a/c + >>>>>>> merge rev: ce36d17b18fb - test: 2 add a/c $ rm b/c.orig Remote directory rename with conflicting file added in remote target directory @@ -177,11 +177,11 @@ ? a/d ? b/c.orig $ cat b/c - <<<<<<< local: ce36d17b18fb - test: 2 add a/c + <<<<<<< working copy: ce36d17b18fb - test: 2 add a/c baz ======= target - >>>>>>> other: f1c50ca4f127 - test: new file in target directory + >>>>>>> merge rev: f1c50ca4f127 - test: new file in target directory Second scenario with two repos:
--- a/tests/test-rename-merge2.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rename-merge2.t Tue Oct 18 14:15:15 2016 -0500 @@ -694,7 +694,7 @@ starting 4 threads for background file closing (?) a: prompt deleted/changed -> m (premerge) picked tool ':prompt' for a (binary False symlink False changedelete True) - remote changed a which local deleted + other [merge rev] changed a which local [working copy] deleted use (c)hanged version, leave (d)eleted, or leave (u)nresolved? u b: both created -> m (premerge) picked tool 'python ../merge' for b (binary False symlink False changedelete False) @@ -719,7 +719,7 @@ -------------- M a M b - abort: unresolved merge conflicts (see "hg help resolve") + abort: unresolved merge conflicts (see 'hg help resolve') -------------- $ tm "up a b" "nm a b" " " "19 merge b no ancestor, prompt remove a" @@ -739,7 +739,7 @@ starting 4 threads for background file closing (?) a: prompt changed/deleted -> m (premerge) picked tool ':prompt' for a (binary False symlink False changedelete True) - local changed a which remote deleted + local [working copy] changed a which other [merge rev] deleted use (c)hanged version, (d)elete, or leave (u)nresolved? u b: both created -> m (premerge) picked tool 'python ../merge' for b (binary False symlink False changedelete False) @@ -764,7 +764,7 @@ -------------- M b C a - abort: unresolved merge conflicts (see "hg help resolve") + abort: unresolved merge conflicts (see 'hg help resolve') -------------- $ tm "up a " "um a b" " " "20 merge a and b to b, remove a"
--- a/tests/test-rename.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-rename.t Tue Oct 18 14:15:15 2016 -0500 @@ -265,7 +265,8 @@ overwrite existing files (d2/b) $ hg rename d1/* d2 - d2/b: not overwriting - file exists + d2/b: not overwriting - file already committed + (hg rename --force to replace the file by recording a rename) moving d1/d11/a1 to d2/d11/a1 (glob) $ hg status -C A d2/a @@ -370,6 +371,7 @@ $ echo "ca" > d1/ca $ hg rename d1/ba d1/ca d1/ca: not overwriting - file exists + (hg rename --after to record the rename) $ hg status -C ? d1/ca $ hg update -C @@ -393,6 +395,7 @@ $ ln -s ba d1/ca $ hg rename --traceback d1/ba d1/ca d1/ca: not overwriting - file exists + (hg rename --after to record the rename) $ hg status -C ? d1/ca $ hg update -C
--- a/tests/test-repair-strip.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-repair-strip.t Tue Oct 18 14:15:15 2016 -0500 @@ -51,7 +51,7 @@ transaction abort! failed to truncate data/b.i rollback failed - please run hg recover - strip failed, full bundle + strip failed, backup bundle abort: Permission denied .hg/store/data/b.i % after update 0, strip 2 abandoned transaction found - run hg recover @@ -104,7 +104,7 @@ transaction abort! failed to truncate 00manifest.i rollback failed - please run hg recover - strip failed, full bundle + strip failed, backup bundle abort: Permission denied .hg/store/00manifest.i % after update 0, strip 2 abandoned transaction found - run hg recover
--- a/tests/test-resolve.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-resolve.t Tue Oct 18 14:15:15 2016 -0500 @@ -290,6 +290,9 @@ * version 2 records local: 57653b9f834a4493f7240b0681efcb9ae7cab745 other: dc77451844e37f03f5c559e3b8529b2b48d381d1 + labels: + local: working copy + other: merge rev unrecognized entry: x advisory record file extras: file1 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac) file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390) @@ -312,6 +315,9 @@ * version 2 records local: 57653b9f834a4493f7240b0681efcb9ae7cab745 other: dc77451844e37f03f5c559e3b8529b2b48d381d1 + labels: + local: working copy + other: merge rev file extras: file1 (ancestorlinknode = 99726c03216e233810a2564cbc0adfe395007eac) file: file1 (record type "F", state "r", hash 60b27f004e454aca81b0480209cce5081ec52390) local path: file1 (flags "")
--- a/tests/test-revset-outgoing.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-revset-outgoing.t Tue Oct 18 14:15:15 2016 -0500 @@ -36,12 +36,12 @@ $ cd b $ cat .hg/hgrc - # example repository config (see "hg help config" for more info) + # example repository config (see 'hg help config' for more info) [paths] default = $TESTTMP/a#stable (glob) # path aliases to other clones of this repo in URLs or filesystem paths - # (see "hg help config.paths" for more info) + # (see 'hg help config.paths' for more info) # # default-push = ssh://jdoe@example.net/hg/jdoes-fork # my-fork = ssh://jdoe@example.net/hg/jdoes-fork @@ -88,12 +88,12 @@ $ echo "green = ../a#default" >> .hg/hgrc $ cat .hg/hgrc - # example repository config (see "hg help config" for more info) + # example repository config (see 'hg help config' for more info) [paths] default = $TESTTMP/a#stable (glob) # path aliases to other clones of this repo in URLs or filesystem paths - # (see "hg help config.paths" for more info) + # (see 'hg help config.paths' for more info) # # default-push = ssh://jdoe@example.net/hg/jdoes-fork # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
--- a/tests/test-revset.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-revset.t Tue Oct 18 14:15:15 2016 -0500 @@ -54,7 +54,7 @@ > tree = revset.parse(expr, lookup=repo.__contains__) > ui.note(revset.prettyformat(tree), "\n") > if opts["optimize"]: - > opttree = revset.optimize(tree) + > opttree = revset.optimize(revset.analyze(tree)) > ui.note("* optimized:\n", revset.prettyformat(opttree), "\n") > func = revset.match(ui, expr, repo) > revs = func(repo) @@ -161,9 +161,9 @@ (rangeall None) * optimized: - (range - ('string', '0') - ('string', 'tip')) + (rangepre + ('string', 'tip') + define) * set: <spanset+ 0:9> 0 @@ -187,9 +187,10 @@ 6 $ try '0|1|2' (or - ('symbol', '0') - ('symbol', '1') - ('symbol', '2')) + (list + ('symbol', '0') + ('symbol', '1') + ('symbol', '2'))) * set: <baseset [0, 1, 2]> 0 @@ -339,10 +340,11 @@ $ log '1&2' $ try '1&2|3' # precedence - and is higher (or - (and - ('symbol', '1') - ('symbol', '2')) - ('symbol', '3')) + (list + (and + ('symbol', '1') + ('symbol', '2')) + ('symbol', '3'))) * set: <addset <baseset []>, @@ -350,10 +352,11 @@ 3 $ try '1|2&3' (or - ('symbol', '1') - (and - ('symbol', '2') - ('symbol', '3'))) + (list + ('symbol', '1') + (and + ('symbol', '2') + ('symbol', '3')))) * set: <addset <baseset [1]>, @@ -369,11 +372,13 @@ <baseset []> $ try '1|(2|3)' (or - ('symbol', '1') - (group - (or - ('symbol', '2') - ('symbol', '3')))) + (list + ('symbol', '1') + (group + (or + (list + ('symbol', '2') + ('symbol', '3')))))) * set: <addset <baseset [1]>, @@ -465,8 +470,9 @@ (keyvalue ('symbol', 'foo') (or - ('symbol', 'bar') - ('symbol', 'baz'))) + (list + ('symbol', 'bar') + ('symbol', 'baz')))) hg: parse error: can't use a key-value pair in this context [255] @@ -485,10 +491,118 @@ ('symbol', 'foo') (func ('symbol', '_notpublic') - None)) + None + any)) hg: parse error: can't use a key-value pair in this context [255] +parsed tree at stages: + + $ hg debugrevspec -p all '()' + * parsed: + (group + None) + * expanded: + (group + None) + * concatenated: + (group + None) + * analyzed: + None + * optimized: + None + hg: parse error: missing argument + [255] + + $ hg debugrevspec --no-optimized -p all '()' + * parsed: + (group + None) + * expanded: + (group + None) + * concatenated: + (group + None) + * analyzed: + None + hg: parse error: missing argument + [255] + + $ hg debugrevspec -p parsed -p analyzed -p optimized '(0|1)-1' + * parsed: + (minus + (group + (or + (list + ('symbol', '0') + ('symbol', '1')))) + ('symbol', '1')) + * analyzed: + (and + (or + (list + ('symbol', '0') + ('symbol', '1')) + define) + (not + ('symbol', '1') + follow) + define) + * optimized: + (difference + (func + ('symbol', '_list') + ('string', '0\x001') + define) + ('symbol', '1') + define) + 0 + + $ hg debugrevspec -p unknown '0' + abort: invalid stage name: unknown + [255] + + $ hg debugrevspec -p all --optimize '0' + abort: cannot use --optimize with --show-stage + [255] + +verify optimized tree: + + $ hg debugrevspec --verify '0|1' + + $ hg debugrevspec --verify -v -p analyzed -p optimized 'r3232() & 2' + * analyzed: + (and + (func + ('symbol', 'r3232') + None + define) + ('symbol', '2') + define) + * optimized: + (and + ('symbol', '2') + (func + ('symbol', 'r3232') + None + define) + define) + * analyzed set: + <baseset [2]> + * optimized set: + <baseset [2, 2]> + --- analyzed + +++ optimized + 2 + +2 + [1] + + $ hg debugrevspec --no-optimized --verify-optimized '0' + abort: cannot use --verify-optimized with --no-optimized + [255] + Test that symbols only get parsed as functions if there's an opening parenthesis. @@ -497,6 +611,215 @@ 8 9 +':y' behaves like '0:y', but can't be rewritten as such since the revision '0' +may be hidden (issue5385) + + $ try -p parsed -p analyzed ':' + * parsed: + (rangeall + None) + * analyzed: + (rangepre + ('string', 'tip') + define) + * set: + <spanset+ 0:9> + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + $ try -p analyzed ':1' + * analyzed: + (rangepre + ('symbol', '1') + define) + * set: + <spanset+ 0:1> + 0 + 1 + $ try -p analyzed ':(1|2)' + * analyzed: + (rangepre + (or + (list + ('symbol', '1') + ('symbol', '2')) + define) + define) + * set: + <spanset+ 0:2> + 0 + 1 + 2 + $ try -p analyzed ':(1&2)' + * analyzed: + (rangepre + (and + ('symbol', '1') + ('symbol', '2') + define) + define) + * set: + <baseset []> + +infix/suffix resolution of ^ operator (issue2884): + + x^:y means (x^):y + + $ try '1^:2' + (range + (parentpost + ('symbol', '1')) + ('symbol', '2')) + * set: + <spanset+ 0:2> + 0 + 1 + 2 + + $ try '1^::2' + (dagrange + (parentpost + ('symbol', '1')) + ('symbol', '2')) + * set: + <baseset+ [0, 1, 2]> + 0 + 1 + 2 + + $ try '9^:' + (rangepost + (parentpost + ('symbol', '9'))) + * set: + <spanset+ 8:9> + 8 + 9 + + x^:y should be resolved before omitting group operators + + $ try '1^(:2)' + (parent + ('symbol', '1') + (group + (rangepre + ('symbol', '2')))) + hg: parse error: ^ expects a number 0, 1, or 2 + [255] + + x^:y should be resolved recursively + + $ try 'sort(1^:2)' + (func + ('symbol', 'sort') + (range + (parentpost + ('symbol', '1')) + ('symbol', '2'))) + * set: + <spanset+ 0:2> + 0 + 1 + 2 + + $ try '(3^:4)^:2' + (range + (parentpost + (group + (range + (parentpost + ('symbol', '3')) + ('symbol', '4')))) + ('symbol', '2')) + * set: + <spanset+ 0:2> + 0 + 1 + 2 + + $ try '(3^::4)^::2' + (dagrange + (parentpost + (group + (dagrange + (parentpost + ('symbol', '3')) + ('symbol', '4')))) + ('symbol', '2')) + * set: + <baseset+ [0, 1, 2]> + 0 + 1 + 2 + + $ try '(9^:)^:' + (rangepost + (parentpost + (group + (rangepost + (parentpost + ('symbol', '9')))))) + * set: + <spanset+ 4:9> + 4 + 5 + 6 + 7 + 8 + 9 + + x^ in alias should also be resolved + + $ try 'A' --config 'revsetalias.A=1^:2' + ('symbol', 'A') + * expanded: + (range + (parentpost + ('symbol', '1')) + ('symbol', '2')) + * set: + <spanset+ 0:2> + 0 + 1 + 2 + + $ try 'A:2' --config 'revsetalias.A=1^' + (range + ('symbol', 'A') + ('symbol', '2')) + * expanded: + (range + (parentpost + ('symbol', '1')) + ('symbol', '2')) + * set: + <spanset+ 0:2> + 0 + 1 + 2 + + but not beyond the boundary of alias expansion, because the resolution should + be made at the parsing stage + + $ try '1^A' --config 'revsetalias.A=:2' + (parent + ('symbol', '1') + ('symbol', 'A')) + * expanded: + (parent + ('symbol', '1') + (rangepre + ('symbol', '2'))) + hg: parse error: ^ expects a number 0, 1, or 2 + [255] + ancestor can accept 0 or more arguments $ log 'ancestor()' @@ -771,8 +1094,11 @@ (difference (range ('symbol', '8') - ('symbol', '9')) - ('symbol', '8'))) + ('symbol', '9') + define) + ('symbol', '8') + define) + define) * set: <baseset+ [8, 9]> 8 @@ -788,7 +1114,8 @@ ('symbol', 'only') (list ('symbol', '9') - ('symbol', '5'))) + ('symbol', '5')) + define) * set: <baseset+ [2, 4, 8, 9]> 2 @@ -984,6 +1311,36 @@ 1 0 + 'x:y' takes ordering parameter into account: + + $ try -p optimized '3:0 & 0:3 & not 2:1' + * optimized: + (difference + (and + (range + ('symbol', '3') + ('symbol', '0') + define) + (range + ('symbol', '0') + ('symbol', '3') + follow) + define) + (range + ('symbol', '2') + ('symbol', '1') + any) + define) + * set: + <filteredset + <filteredset + <spanset- 0:3>, + <spanset+ 0:3>>, + <not + <spanset+ 1:2>>> + 3 + 0 + 'a + b', which is optimized to '_list(a b)', should take the ordering of the left expression: @@ -994,23 +1351,28 @@ ('symbol', '0')) (group (or - ('symbol', '0') - ('symbol', '1') - ('symbol', '2')))) + (list + ('symbol', '0') + ('symbol', '1') + ('symbol', '2'))))) * optimized: (and (range ('symbol', '2') - ('symbol', '0')) + ('symbol', '0') + define) (func ('symbol', '_list') - ('string', '0\x001\x002'))) + ('string', '0\x001\x002') + follow) + define) * set: - <baseset [0, 1, 2]> + <filteredset + <spanset- 0:2>, + <baseset [0, 1, 2]>> + 2 + 1 0 - 1 - 2 - BROKEN: should be '2 1 0' 'A + B' should take the ordering of the left expression: @@ -1021,30 +1383,35 @@ ('symbol', '0')) (group (or - (range - ('symbol', '0') - ('symbol', '1')) - ('symbol', '2')))) + (list + (range + ('symbol', '0') + ('symbol', '1')) + ('symbol', '2'))))) * optimized: (and (range ('symbol', '2') - ('symbol', '0')) + ('symbol', '0') + define) (or - (range - ('symbol', '0') - ('symbol', '1')) - ('symbol', '2'))) + (list + (range + ('symbol', '0') + ('symbol', '1') + follow) + ('symbol', '2')) + follow) + define) * set: - <addset - <filteredset + <filteredset + <spanset- 0:2>, + <addset <spanset+ 0:1>, - <spanset- 0:2>>, - <baseset [2]>> + <baseset [2]>>> + 2 + 1 0 - 1 - 2 - BROKEN: should be '2 1 0' '_intlist(a b)' should behave like 'a + b': @@ -1060,14 +1427,17 @@ (and (func ('symbol', '_intlist') - ('string', '0\x001\x002')) + ('string', '0\x001\x002') + follow) (range ('symbol', '2') - ('symbol', '0'))) + ('symbol', '0') + define) + define) * set: <filteredset <spanset- 0:2>, - <baseset [0, 1, 2]>> + <baseset+ [0, 1, 2]>> 2 1 0 @@ -1084,18 +1454,20 @@ (and (func ('symbol', '_intlist') - ('string', '0\x002\x001')) + ('string', '0\x002\x001') + define) (range ('symbol', '2') - ('symbol', '0'))) + ('symbol', '0') + follow) + define) * set: <filteredset - <spanset- 0:2>, - <baseset [0, 2, 1]>> + <baseset [0, 2, 1]>, + <spanset- 0:2>> + 0 2 1 - 0 - BROKEN: should be '0 2 1' '_hexlist(a b)' should behave like 'a + b': @@ -1111,16 +1483,20 @@ (and (range ('symbol', '2') - ('symbol', '0')) + ('symbol', '0') + define) (func ('symbol', '_hexlist') - ('string', '*'))) (glob) + ('string', '*') (glob) + follow) + define) * set: - <baseset [0, 1, 2]> + <filteredset + <spanset- 0:2>, + <baseset [0, 1, 2]>> + 2 + 1 0 - 1 - 2 - BROKEN: should be '2 1 0' $ trylist --optimize --bin '%ln & 2:0' `hg log -T '{node} ' -r0+2+1` (and @@ -1134,17 +1510,83 @@ (and (range ('symbol', '2') - ('symbol', '0')) + ('symbol', '0') + follow) (func ('symbol', '_hexlist') - ('string', '*'))) (glob) + ('string', '*') (glob) + define) + define) * set: <baseset [0, 2, 1]> 0 2 1 - 'present()' should do nothing other than suppressing an error: + '_list' should not go through the slow follow-order path if order doesn't + matter: + + $ try -p optimized '2:0 & not (0 + 1)' + * optimized: + (difference + (range + ('symbol', '2') + ('symbol', '0') + define) + (func + ('symbol', '_list') + ('string', '0\x001') + any) + define) + * set: + <filteredset + <spanset- 0:2>, + <not + <baseset [0, 1]>>> + 2 + + $ try -p optimized '2:0 & not (0:2 & (0 + 1))' + * optimized: + (difference + (range + ('symbol', '2') + ('symbol', '0') + define) + (and + (range + ('symbol', '0') + ('symbol', '2') + any) + (func + ('symbol', '_list') + ('string', '0\x001') + any) + any) + define) + * set: + <filteredset + <spanset- 0:2>, + <not + <baseset [0, 1]>>> + 2 + + because 'present()' does nothing other than suppressing an error, the + ordering requirement should be forwarded to the nested expression + + $ try -p optimized 'present(2 + 0 + 1)' + * optimized: + (func + ('symbol', 'present') + (func + ('symbol', '_list') + ('string', '2\x000\x001') + define) + define) + * set: + <baseset [2, 0, 1]> + 2 + 0 + 1 $ try --optimize '2:0 & present(0 + 1 + 2)' (and @@ -1154,25 +1596,31 @@ (func ('symbol', 'present') (or - ('symbol', '0') - ('symbol', '1') - ('symbol', '2')))) + (list + ('symbol', '0') + ('symbol', '1') + ('symbol', '2'))))) * optimized: (and (range ('symbol', '2') - ('symbol', '0')) + ('symbol', '0') + define) (func ('symbol', 'present') (func ('symbol', '_list') - ('string', '0\x001\x002')))) + ('string', '0\x001\x002') + follow) + follow) + define) * set: - <baseset [0, 1, 2]> + <filteredset + <spanset- 0:2>, + <baseset [0, 1, 2]>> + 2 + 1 0 - 1 - 2 - BROKEN: should be '2 1 0' 'reverse()' should take effect only if it is the outermost expression: @@ -1190,20 +1638,23 @@ (and (range ('symbol', '0') - ('symbol', '2')) + ('symbol', '2') + define) (func ('symbol', 'reverse') (func ('symbol', 'all') - None))) + None + define) + follow) + define) * set: <filteredset - <spanset- 0:2>, + <spanset+ 0:2>, <spanset+ 0:9>> + 0 + 1 2 - 1 - 0 - BROKEN: should be '0 1 2' 'sort()' should take effect only if it is the outermost expression: @@ -1224,22 +1675,34 @@ (and (range ('symbol', '0') - ('symbol', '2')) + ('symbol', '2') + define) (func ('symbol', 'sort') (list (func ('symbol', 'all') - None) - ('string', '-rev')))) + None + define) + ('string', '-rev')) + follow) + define) * set: <filteredset - <spanset- 0:2>, + <spanset+ 0:2>, <spanset+ 0:9>> + 0 + 1 2 - 1 - 0 - BROKEN: should be '0 1 2' + + invalid argument passed to noop sort(): + + $ log '0:2 & sort()' + hg: parse error: sort requires one or two arguments + [255] + $ log '0:2 & sort(all(), -invalid)' + hg: parse error: unknown sort key '-invalid' + [255] for 'A & f(B)', 'B' should not be affected by the order of 'A': @@ -1251,19 +1714,24 @@ (func ('symbol', 'first') (or - ('symbol', '1') - ('symbol', '0') - ('symbol', '2')))) + (list + ('symbol', '1') + ('symbol', '0') + ('symbol', '2'))))) * optimized: (and (range ('symbol', '2') - ('symbol', '0')) + ('symbol', '0') + define) (func ('symbol', 'first') (func ('symbol', '_list') - ('string', '1\x000\x002')))) + ('string', '1\x000\x002') + define) + follow) + define) * set: <baseset <limit n=1, offset=0, @@ -1280,19 +1748,24 @@ (func ('symbol', 'last') (or - ('symbol', '0') - ('symbol', '2') - ('symbol', '1'))))) + (list + ('symbol', '0') + ('symbol', '2') + ('symbol', '1')))))) * optimized: (difference (range ('symbol', '2') - ('symbol', '0')) + ('symbol', '0') + define) (func ('symbol', 'last') (func ('symbol', '_list') - ('string', '0\x002\x001')))) + ('string', '0\x002\x001') + define) + any) + define) * set: <filteredset <spanset- 0:2>, @@ -1314,35 +1787,43 @@ (range (group (or - ('symbol', '1') - ('symbol', '0') - ('symbol', '2'))) + (list + ('symbol', '1') + ('symbol', '0') + ('symbol', '2')))) (group (or - ('symbol', '0') - ('symbol', '2') - ('symbol', '1'))))) + (list + ('symbol', '0') + ('symbol', '2') + ('symbol', '1')))))) * optimized: (and (range ('symbol', '2') - ('symbol', '0')) + ('symbol', '0') + define) (range (func ('symbol', '_list') - ('string', '1\x000\x002')) + ('string', '1\x000\x002') + define) (func ('symbol', '_list') - ('string', '0\x002\x001')))) + ('string', '0\x002\x001') + define) + follow) + define) * set: <filteredset - <baseset [1]>, - <spanset- 0:2>> + <spanset- 0:2>, + <baseset [1]>> 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'): + 'A & B' can be rewritten as 'B & A' by weight, but that's fine as long as + the ordering rule is determined before the rewrite; in this example, + 'B' follows the order of the initial set, which is the same order as 'A' + since 'A' also follows the order: $ try --optimize 'contains("glob:*") & (2 + 0 + 1)' (and @@ -1351,25 +1832,31 @@ ('string', 'glob:*')) (group (or - ('symbol', '2') - ('symbol', '0') - ('symbol', '1')))) + (list + ('symbol', '2') + ('symbol', '0') + ('symbol', '1'))))) * optimized: (and (func ('symbol', '_list') - ('string', '2\x000\x001')) + ('string', '2\x000\x001') + follow) (func ('symbol', 'contains') - ('string', 'glob:*'))) + ('string', 'glob:*') + define) + define) * set: <filteredset - <baseset [2, 0, 1]>, + <baseset+ [0, 1, 2]>, <contains 'glob:*'>> - 2 0 1 - BROKEN: should be '0 1 2' + 2 + + and in this example, 'A & B' is rewritten as 'B & A', but 'A' overrides + the order appropriately: $ try --optimize 'reverse(contains("glob:*")) & (0 + 2 + 1)' (and @@ -1380,27 +1867,31 @@ ('string', 'glob:*'))) (group (or - ('symbol', '0') - ('symbol', '2') - ('symbol', '1')))) + (list + ('symbol', '0') + ('symbol', '2') + ('symbol', '1'))))) * optimized: (and (func ('symbol', '_list') - ('string', '0\x002\x001')) + ('string', '0\x002\x001') + follow) (func ('symbol', 'reverse') (func ('symbol', 'contains') - ('string', 'glob:*')))) + ('string', 'glob:*') + define) + define) + define) * set: <filteredset - <baseset [1, 2, 0]>, + <baseset- [0, 1, 2]>, <contains 'glob:*'>> + 2 1 - 2 0 - BROKEN: should be '2 1 0' test sort revset -------------------------------------------- @@ -1727,14 +2218,15 @@ $ try 'reverse(1::5) or ancestors(4)' (or - (func - ('symbol', 'reverse') - (dagrange - ('symbol', '1') - ('symbol', '5'))) - (func - ('symbol', 'ancestors') - ('symbol', '4'))) + (list + (func + ('symbol', 'reverse') + (dagrange + ('symbol', '1') + ('symbol', '5'))) + (func + ('symbol', 'ancestors') + ('symbol', '4')))) * set: <addset <baseset- [1, 3, 5]>, @@ -1749,14 +2241,15 @@ (func ('symbol', 'sort') (or - (func - ('symbol', 'ancestors') - ('symbol', '4')) - (func - ('symbol', 'reverse') - (dagrange - ('symbol', '1') - ('symbol', '5'))))) + (list + (func + ('symbol', 'ancestors') + ('symbol', '4')) + (func + ('symbol', 'reverse') + (dagrange + ('symbol', '1') + ('symbol', '5')))))) * set: <addset+ <generatorset+>, @@ -1772,18 +2265,20 @@ $ try --optimize '0|(1)|"2"|-2|tip|null' (or - ('symbol', '0') - (group - ('symbol', '1')) - ('string', '2') - (negate - ('symbol', '2')) - ('symbol', 'tip') - ('symbol', 'null')) + (list + ('symbol', '0') + (group + ('symbol', '1')) + ('string', '2') + (negate + ('symbol', '2')) + ('symbol', 'tip') + ('symbol', 'null'))) * optimized: (func ('symbol', '_list') - ('string', '0\x001\x002\x00-2\x00tip\x00null')) + ('string', '0\x001\x002\x00-2\x00tip\x00null') + define) * set: <baseset [0, 1, 2, 8, 9, -1]> 0 @@ -1795,19 +2290,24 @@ $ try --optimize '0|1|2:3' (or - ('symbol', '0') - ('symbol', '1') - (range - ('symbol', '2') - ('symbol', '3'))) + (list + ('symbol', '0') + ('symbol', '1') + (range + ('symbol', '2') + ('symbol', '3')))) * optimized: (or - (func - ('symbol', '_list') - ('string', '0\x001')) - (range - ('symbol', '2') - ('symbol', '3'))) + (list + (func + ('symbol', '_list') + ('string', '0\x001') + define) + (range + ('symbol', '2') + ('symbol', '3') + define)) + define) * set: <addset <baseset [0, 1]>, @@ -1819,27 +2319,33 @@ $ try --optimize '0:1|2|3:4|5|6' (or - (range - ('symbol', '0') - ('symbol', '1')) - ('symbol', '2') - (range - ('symbol', '3') - ('symbol', '4')) - ('symbol', '5') - ('symbol', '6')) + (list + (range + ('symbol', '0') + ('symbol', '1')) + ('symbol', '2') + (range + ('symbol', '3') + ('symbol', '4')) + ('symbol', '5') + ('symbol', '6'))) * optimized: (or - (range - ('symbol', '0') - ('symbol', '1')) - ('symbol', '2') - (range - ('symbol', '3') - ('symbol', '4')) - (func - ('symbol', '_list') - ('string', '5\x006'))) + (list + (range + ('symbol', '0') + ('symbol', '1') + define) + ('symbol', '2') + (range + ('symbol', '3') + ('symbol', '4') + define) + (func + ('symbol', '_list') + ('string', '5\x006') + define)) + define) * set: <addset <addset @@ -1856,6 +2362,34 @@ 5 6 +unoptimized `or` looks like this + + $ try --no-optimized -p analyzed '0|1|2|3|4' + * analyzed: + (or + (list + ('symbol', '0') + ('symbol', '1') + ('symbol', '2') + ('symbol', '3') + ('symbol', '4')) + define) + * set: + <addset + <addset + <baseset [0]>, + <baseset [1]>>, + <addset + <baseset [2]>, + <addset + <baseset [3]>, + <baseset [4]>>>> + 0 + 1 + 2 + 3 + 4 + test that `_list` should be narrowed by provided `subset` $ log '0:2 and (null|1|2|3)' @@ -1914,21 +2448,22 @@ $ try '0:1|1:2|2:3|3:4|4:5' (or - (range - ('symbol', '0') - ('symbol', '1')) - (range - ('symbol', '1') - ('symbol', '2')) - (range - ('symbol', '2') - ('symbol', '3')) - (range - ('symbol', '3') - ('symbol', '4')) - (range - ('symbol', '4') - ('symbol', '5'))) + (list + (range + ('symbol', '0') + ('symbol', '1')) + (range + ('symbol', '1') + ('symbol', '2')) + (range + ('symbol', '2') + ('symbol', '3')) + (range + ('symbol', '3') + ('symbol', '4')) + (range + ('symbol', '4') + ('symbol', '5')))) * set: <addset <addset @@ -1950,13 +2485,16 @@ $ try --optimize '0|()' (or - ('symbol', '0') - (group - None)) + (list + ('symbol', '0') + (group + None))) * optimized: (or - ('symbol', '0') - None) + (list + ('symbol', '0') + None) + define) hg: parse error: missing argument [255] @@ -1986,7 +2524,8 @@ ('symbol', 'only') (list ('symbol', '3') - ('symbol', '1'))) + ('symbol', '1')) + define) * set: <baseset+ [3]> 3 @@ -2003,7 +2542,8 @@ ('symbol', 'only') (list ('symbol', '1') - ('symbol', '3'))) + ('symbol', '3')) + define) * set: <baseset+ []> $ try --optimize 'not ::2 and ::6' @@ -2018,7 +2558,8 @@ ('symbol', 'only') (list ('symbol', '6') - ('symbol', '2'))) + ('symbol', '2')) + define) * set: <baseset+ [3, 4, 5, 6]> 3 @@ -2039,7 +2580,8 @@ ('symbol', 'only') (list ('symbol', '6') - ('symbol', '4'))) + ('symbol', '4')) + define) * set: <baseset+ [3, 5, 6]> 3 @@ -2059,7 +2601,9 @@ None (func ('symbol', 'ancestors') - ('symbol', '1'))) + ('symbol', '1') + define) + define) hg: parse error: missing argument [255] @@ -2206,6 +2750,7 @@ 5 $ log 'merge()^2' 4 + $ log '(not merge())^2' $ log 'merge()^^' 3 $ log 'merge()^1^' @@ -2441,10 +2986,12 @@ ('symbol', '3'))) * expanded: (or - ('symbol', '3') - (or - ('symbol', '1') - ('symbol', '2'))) + (list + ('symbol', '3') + (or + (list + ('symbol', '1') + ('symbol', '2'))))) * set: <addset <baseset [3]>, @@ -2495,15 +3042,16 @@ ('symbol', '3')))) * expanded: (or - (range - ('symbol', '0') - ('symbol', '1')) - (range - ('symbol', '1') - ('symbol', '2')) - (range - ('symbol', '2') - ('symbol', '3'))) + (list + (range + ('symbol', '0') + ('symbol', '1')) + (range + ('symbol', '1') + ('symbol', '2')) + (range + ('symbol', '2') + ('symbol', '3')))) * set: <addset <spanset+ 0:1>, @@ -2594,10 +3142,11 @@ ('symbol', 'tip'))) * expanded: (or - ('symbol', 'tip') - (func - ('symbol', 'desc') - ('string', '$1'))) + (list + ('symbol', 'tip') + (func + ('symbol', 'desc') + ('string', '$1')))) * set: <addset <baseset [9]>, @@ -2633,8 +3182,9 @@ ('symbol', 'rs') (list (or - ('symbol', '2') - ('symbol', '3')) + (list + ('symbol', '2') + ('symbol', '3'))) ('symbol', 'date'))) * expanded: (func @@ -2643,8 +3193,9 @@ ('symbol', 'sort') (list (or - ('symbol', '2') - ('symbol', '3')) + (list + ('symbol', '2') + ('symbol', '3'))) ('symbol', 'date')))) * set: <baseset [3, 2]> @@ -2676,8 +3227,9 @@ ('symbol', 'rs4') (list (or - ('symbol', '2') - ('symbol', '3')) + (list + ('symbol', '2') + ('symbol', '3'))) ('symbol', 'x') ('symbol', 'x') ('symbol', 'date'))) @@ -2688,8 +3240,9 @@ ('symbol', 'sort') (list (or - ('symbol', '2') - ('symbol', '3')) + (list + ('symbol', '2') + ('symbol', '3'))) ('symbol', 'date')))) * set: <baseset [3, 2]> @@ -2760,9 +3313,10 @@ ('symbol', 'limit') (list (or - ('symbol', '1') - ('symbol', '2') - ('symbol', '3')) + (list + ('symbol', '1') + ('symbol', '2') + ('symbol', '3'))) ('symbol', '2'))) (not ('symbol', '2'))) @@ -2780,8 +3334,9 @@ (func ('symbol', 'max') (or - ('symbol', '1') - ('symbol', '2'))) + (list + ('symbol', '1') + ('symbol', '2')))) (not ('symbol', '2'))) * set: @@ -2797,8 +3352,9 @@ (func ('symbol', 'min') (or - ('symbol', '1') - ('symbol', '2'))) + (list + ('symbol', '1') + ('symbol', '2')))) (not ('symbol', '1'))) * set: @@ -2815,8 +3371,9 @@ ('symbol', 'last') (list (or - ('symbol', '1') - ('symbol', '2')) + (list + ('symbol', '1') + ('symbol', '2'))) ('symbol', '1'))) (not ('symbol', '2')))
--- a/tests/test-shelve.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-shelve.t Tue Oct 18 14:15:15 2016 -0500 @@ -54,7 +54,7 @@ To delete specific shelved changes, use "--delete". To delete all shelved changes, use "--cleanup". - (use "hg help -e shelve" to show help for the shelve extension) + (use 'hg help -e shelve' to show help for the shelve extension) options ([+] can be repeated):
--- a/tests/test-ssh-bundle1.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-ssh-bundle1.t Tue Oct 18 14:15:15 2016 -0500 @@ -2,9 +2,9 @@ parts that are not bundle1/bundle2 specific. $ cat << EOF >> $HGRCPATH - > [experimental] + > [devel] > # This test is dedicated to interaction through old bundle - > bundle2-exp = False + > legacy.exchange = bundle1 > [format] # temporary settings > usegeneraldelta=yes > EOF @@ -60,8 +60,8 @@ $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream streaming all changes - 4 files to transfer, 615 bytes of data - transferred 615 bytes in * seconds (*) (glob) + 4 files to transfer, 602 bytes of data + transferred 602 bytes in * seconds (*) (glob) searching for changes no changes found updating to branch default @@ -82,8 +82,8 @@ $ hg -R local-stream book mybook $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/local-stream stream2 streaming all changes - 4 files to transfer, 615 bytes of data - transferred 615 bytes in * seconds (*) (glob) + 4 files to transfer, 602 bytes of data + transferred 602 bytes in * seconds (*) (glob) searching for changes no changes found updating to branch default
--- a/tests/test-ssh.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-ssh.t Tue Oct 18 14:15:15 2016 -0500 @@ -54,8 +54,8 @@ $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/remote local-stream streaming all changes - 4 files to transfer, 615 bytes of data - transferred 615 bytes in * seconds (*) (glob) + 4 files to transfer, 602 bytes of data + transferred 602 bytes in * seconds (*) (glob) searching for changes no changes found updating to branch default @@ -76,8 +76,8 @@ $ hg -R local-stream book mybook $ hg clone -e "python \"$TESTDIR/dummyssh\"" --uncompressed ssh://user@dummy/local-stream stream2 streaming all changes - 4 files to transfer, 615 bytes of data - transferred 615 bytes in * seconds (*) (glob) + 4 files to transfer, 602 bytes of data + transferred 602 bytes in * seconds (*) (glob) searching for changes no changes found updating to branch default
--- a/tests/test-status-color.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-status-color.t Tue Oct 18 14:15:15 2016 -0500 @@ -237,6 +237,25 @@ \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc) \x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc) +The user can define effects with raw terminfo codes: + + $ cat <<EOF >> $HGRCPATH + > # Completely bogus code for dim + > terminfo.dim = \E[88m + > # We can override what's in the terminfo database, too + > terminfo.bold = \E[2m + > EOF + $ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --config color.status.clean=dim --color=always -A + \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2madded\x1b[30m (esc) + \x1b[30m\x1b[32m\x1b[2mA \x1b[30m\x1b[30m\x1b[32m\x1b[2mcopied\x1b[30m (esc) + \x1b[30m\x1b[30m modified\x1b[30m (esc) + \x1b[30m\x1b[31m\x1b[2mR \x1b[30m\x1b[30m\x1b[31m\x1b[2mremoved\x1b[30m (esc) + \x1b[30m\x1b[36m\x1b[2m\x1b[4m! \x1b[30m\x1b[30m\x1b[36m\x1b[2m\x1b[4mdeleted\x1b[30m (esc) + \x1b[30m\x1b[35m\x1b[2m\x1b[4m? \x1b[30m\x1b[30m\x1b[35m\x1b[2m\x1b[4munknown\x1b[30m (esc) + \x1b[30m\x1b[30m\x1b[2mI \x1b[30m\x1b[30m\x1b[30m\x1b[2mignored\x1b[30m (esc) + \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88m.hgignore\x1b[30m (esc) + \x1b[30m\x1b[88mC \x1b[30m\x1b[30m\x1b[88mmodified\x1b[30m (esc) + #endif
--- a/tests/test-strict.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-strict.t Tue Oct 18 14:15:15 2016 -0500 @@ -37,7 +37,7 @@ summary summarize working directory state update update working directory (or switch revisions) - (use "hg help" for the full list of commands or "hg -v" for details) + (use 'hg help' for the full list of commands or 'hg -v' for details) [255] $ hg annotate a 0: a
--- a/tests/test-strip.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-strip.t Tue Oct 18 14:15:15 2016 -0500 @@ -367,11 +367,51 @@ date: Thu Jan 01 00:00:00 1970 +0000 summary: a +Failed hook while applying "saveheads" bundle. + + $ hg strip 2 --config hooks.pretxnchangegroup.bad=false + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) + transaction abort! + rollback completed + strip failed, backup bundle stored in '$TESTTMP/test/.hg/strip-backup/*-backup.hg' (glob) + strip failed, unrecovered changes stored in '$TESTTMP/test/.hg/strip-backup/*-temp.hg' (glob) + (fix the problem, then recover the changesets with "hg unbundle '$TESTTMP/test/.hg/strip-backup/*-temp.hg'") (glob) + abort: pretxnchangegroup.bad hook exited with status 1 + [255] + $ restore + $ hg log -G + o changeset: 4:443431ffac4f + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: e + | + o changeset: 3:65bd5f99a4a3 + | parent: 1:ef3a871183d7 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: d + | + | o changeset: 2:264128213d29 + |/ user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: c + | + @ changeset: 1:ef3a871183d7 + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: b + | + o changeset: 0:9ab35a2d17cb + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: a + 2 different branches: 2 strips $ hg strip 2 4 - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob) $ hg log -G o changeset: 2:65bd5f99a4a3 @@ -664,7 +704,7 @@ strip changesets and all their descendants from the repository - (use "hg help -e strip" to show help for the strip extension) + (use 'hg help -e strip' to show help for the strip extension) options ([+] can be repeated): @@ -677,7 +717,7 @@ -B --bookmark VALUE [+] remove revs only reachable from given bookmark --mq operate on patch repository - (use "hg strip -h" to show more help) + (use 'hg strip -h' to show more help) [255] $ cd .. @@ -891,7 +931,7 @@ > EOF $ hg strip tip --config extensions.crash=$TESTTMP/crashstrip.py saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg (glob) - strip failed, full bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg' (glob) + strip failed, backup bundle stored in '$TESTTMP/issue4736/.hg/strip-backup/5c51d8d6557d-70daef06-backup.hg' (glob) abort: boom [255]
--- a/tests/test-subrepo-deep-nested-change.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-subrepo-deep-nested-change.t Tue Oct 18 14:15:15 2016 -0500 @@ -127,12 +127,12 @@ Largefiles is NOT enabled in the clone if the source repo doesn't require it $ cat cloned/.hg/hgrc - # example repository config (see "hg help config" for more info) + # example repository config (see 'hg help config' for more info) [paths] default = $TESTTMP/main (glob) # path aliases to other clones of this repo in URLs or filesystem paths - # (see "hg help config.paths" for more info) + # (see 'hg help config.paths' for more info) # # default-push = ssh://jdoe@example.net/hg/jdoes-fork # my-fork = ssh://jdoe@example.net/hg/jdoes-fork @@ -688,12 +688,12 @@ [255] $ hg --config extensions.largefiles= clone -qU . ../lfclone $ cat ../lfclone/.hg/hgrc - # example repository config (see "hg help config" for more info) + # example repository config (see 'hg help config' for more info) [paths] default = $TESTTMP/cloned (glob) # path aliases to other clones of this repo in URLs or filesystem paths - # (see "hg help config.paths" for more info) + # (see 'hg help config.paths' for more info) # # default-push = ssh://jdoe@example.net/hg/jdoes-fork # my-fork = ssh://jdoe@example.net/hg/jdoes-fork
--- a/tests/test-subrepo-git.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-subrepo-git.t Tue Oct 18 14:15:15 2016 -0500 @@ -171,7 +171,7 @@ (run 'hg heads' to see heads, 'hg merge' to merge) $ hg merge 2>/dev/null subrepository s diverged (local revision: 7969594, remote revision: aa84837) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m pulling subrepo s from $TESTTMP/gitroot 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -507,7 +507,7 @@ $ cd .. $ hg update 4 subrepository s diverged (local revision: da5f5b1, remote revision: aa84837) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for s differ use (l)ocal source (da5f5b1) or (r)emote source (aa84837)? l 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -533,7 +533,7 @@ $ cd .. $ hg update 1 subrepository s diverged (local revision: 32a3438, remote revision: da5f5b1) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for s differ (in checked out version) use (l)ocal source (32a3438) or (r)emote source (da5f5b1)? l 1 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -555,7 +555,7 @@ 1+ $ hg update 7 subrepository s diverged (local revision: 32a3438, remote revision: 32a3438) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for s differ use (l)ocal source (32a3438) or (r)emote source (32a3438)? l 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-subrepo-missing.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-subrepo-missing.t Tue Oct 18 14:15:15 2016 -0500 @@ -62,7 +62,7 @@ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ rm .hgsubstate $ hg up 0 - remote changed .hgsubstate which local deleted + other [destination] changed .hgsubstate which local [working copy] deleted use (c)hanged version or leave (d)eleted? c 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg st
--- a/tests/test-subrepo-svn.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-subrepo-svn.t Tue Oct 18 14:15:15 2016 -0500 @@ -310,7 +310,7 @@ $ cd .. $ hg update tip subrepository s diverged (local revision: 2, remote revision: 3) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for s differ use (l)ocal source (2) or (r)emote source (3)? l 1 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -341,7 +341,7 @@ $ cd .. $ hg update 1 subrepository s diverged (local revision: 3, remote revision: 2) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for s differ (in checked out version) use (l)ocal source (1) or (r)emote source (2)? l 1 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -364,7 +364,7 @@ 1+ $ hg update tip subrepository s diverged (local revision: 3, remote revision: 3) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for s differ use (l)ocal source (1) or (r)emote source (3)? l 1 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -398,7 +398,7 @@ $ cd .. $ hg update 1 subrepository s diverged (local revision: 3, remote revision: 2) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg id -n 1+
--- a/tests/test-subrepo.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-subrepo.t Tue Oct 18 14:15:15 2016 -0500 @@ -303,14 +303,14 @@ subrepo merge e45c8b14af55+ f94576341bcf 1831e14459c4 subrepo t: both sides changed subrepository t diverged (local revision: 20a0db6fbf6c, remote revision: 7af322bc1198) - (M)erge, keep (l)ocal or keep (r)emote? m + starting 4 threads for background file closing (?) + (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m merging subrepo t searching for copies back to rev 2 resolving manifests branchmerge: True, force: False, partial: False ancestor: 6747d179aa9a, local: 20a0db6fbf6c+, remote: 7af322bc1198 preserving t for resolve of t - starting 4 threads for background file closing (?) t: versions differ -> m (premerge) picked tool ':merge' for t (binary False symlink False changedelete False) merging t @@ -349,7 +349,7 @@ local removed, remote changed, keep changed $ hg merge 6 - remote changed subrepository t which local removed + remote [merge rev] changed subrepository s which local [working copy] removed use (c)hanged version or (d)elete? c 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -380,7 +380,7 @@ $ hg merge --config ui.interactive=true 6 <<EOF > d > EOF - remote changed subrepository t which local removed + remote [merge rev] changed subrepository s which local [working copy] removed use (c)hanged version or (d)elete? d 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -404,7 +404,7 @@ $ hg co -C 6 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg merge 11 - local changed subrepository t which remote removed + local [working copy] changed subrepository s which remote [merge rev] removed use (c)hanged version or (d)elete? c 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -436,7 +436,7 @@ $ hg merge --config ui.interactive=true 11 <<EOF > d > EOF - local changed subrepository t which remote removed + local [working copy] changed subrepository s which remote [merge rev] removed use (c)hanged version or (d)elete? d 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) @@ -516,7 +516,7 @@ pushing subrepo s to $TESTTMP/t/s searching for changes abort: push creates new remote head 12a213df6fa9! (in subrepo s) - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg push -f pushing to $TESTTMP/t (glob) @@ -860,7 +860,7 @@ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg merge 4 # try to merge default into br again subrepository s diverged (local revision: f8f13b33206e, remote revision: a3f9062a4f88) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [merge rev]? m 1 files updated, 0 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ cd .. @@ -952,7 +952,7 @@ $ hg -R repo2 ci -m3 $ hg -q -R repo2 push abort: push creates new remote head cc505f09a8b2! (in subrepo s) - (merge or see "hg help push" for details about pushing new heads) + (merge or see 'hg help push' for details about pushing new heads) [255] $ hg -R repo update 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -1129,7 +1129,7 @@ adding file changes added 1 changesets with 2 changes to 2 files subrepository sub/repo diverged (local revision: f42d5c7504a8, remote revision: 46cd4aac504c) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m pulling subrepo sub/repo from $TESTTMP/issue1852a/sub/repo (glob) searching for changes adding changesets @@ -1238,11 +1238,11 @@ e95bcfa18a35+ $ hg update tip subrepository s diverged (local revision: fc627a69481f, remote revision: 12a213df6fa9) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for s differ use (l)ocal source (fc627a69481f) or (r)emote source (12a213df6fa9)? l subrepository t diverged (local revision: e95bcfa18a35, remote revision: 52c0adc0515a) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for t differ use (l)ocal source (e95bcfa18a35) or (r)emote source (52c0adc0515a)? l 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -1271,9 +1271,9 @@ $ cd .. $ hg update 10 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository t diverged (local revision: 52c0adc0515a, remote revision: 20a0db6fbf6c) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for t differ (in checked out version) use (l)ocal source (7af322bc1198) or (r)emote source (20a0db6fbf6c)? l 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -1297,11 +1297,11 @@ 7af322bc1198+ $ hg update tip subrepository s diverged (local revision: 12a213df6fa9, remote revision: 12a213df6fa9) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for s differ use (l)ocal source (02dcf1d70411) or (r)emote source (12a213df6fa9)? l subrepository t diverged (local revision: 52c0adc0515a, remote revision: 52c0adc0515a) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m subrepository sources for t differ use (l)ocal source (7af322bc1198) or (r)emote source (52c0adc0515a)? l 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -1329,7 +1329,7 @@ $ cd .. $ hg update 11 subrepository s diverged (local revision: 12a213df6fa9, remote revision: fc627a69481f) - (M)erge, keep (l)ocal or keep (r)emote? m + (M)erge, keep (l)ocal [working copy] or keep (r)emote [destination]? m 0 files updated, 0 files merged, 0 files removed, 0 files unresolved 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg id -n @@ -1529,7 +1529,7 @@ > [paths] > default=../issue3781-dest/ > EOF - $ hg push --config experimental.bundle2-exp=False + $ hg push --config devel.legacy.exchange=bundle1 pushing to $TESTTMP/issue3781-dest (glob) pushing subrepo s to $TESTTMP/issue3781-dest/s searching for changes @@ -1539,7 +1539,7 @@ [1] # clean the push cache $ rm s/.hg/cache/storehash/* - $ hg push --config experimental.bundle2-exp=True + $ hg push # bundle2+ pushing to $TESTTMP/issue3781-dest (glob) pushing subrepo s to $TESTTMP/issue3781-dest/s searching for changes
--- a/tests/test-tags.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-tags.t Tue Oct 18 14:15:15 2016 -0500 @@ -645,10 +645,6 @@ $ hg init tagsserver $ cd tagsserver - $ cat > .hg/hgrc << EOF - > [experimental] - > bundle2-exp=True - > EOF $ touch foo $ hg -q commit -A -m initial $ hg tag -m 'tag 0.1' 0.1 @@ -663,7 +659,7 @@ Cloning should pull down hgtags fnodes mappings and write the cache file - $ hg --config experimental.bundle2-exp=True clone --pull tagsserver tagsclient + $ hg clone --pull tagsserver tagsclient requesting all changes adding changesets adding manifests
--- a/tests/test-treemanifest.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-treemanifest.t Tue Oct 18 14:15:15 2016 -0500 @@ -62,6 +62,19 @@ dir1/dir2/b (glob) e +The manifest command works + + $ hg manifest + a + b + dir1/a + dir1/b + dir1/dir1/a + dir1/dir1/b + dir1/dir2/a + dir1/dir2/b + e + Revision is not created for unchanged directory $ mkdir dir2 @@ -313,6 +326,25 @@ rev offset length delta linkrev nodeid p1 p2 0 0 127 -1 4 064927a0648a 000000000000 000000000000 1 127 111 0 5 25ecb8cb8618 000000000000 000000000000 + $ hg incoming .hg/strip-backup/* + comparing with .hg/strip-backup/*-backup.hg (glob) + searching for changes + changeset: 6:51cfd7b1e13b + tag: tip + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: modify dir1/a + + $ hg pull .hg/strip-backup/* + pulling from .hg/strip-backup/51cfd7b1e13b-78a2f3ed-backup.hg + 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 --config extensions.strip= strip tip + saved backup bundle to $TESTTMP/repo-mixed/.hg/strip-backup/*-backup.hg (glob) $ hg unbundle -q .hg/strip-backup/* $ hg debugindex --dir dir1 rev offset length delta linkrev nodeid p1 p2
--- a/tests/test-unbundlehash.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-unbundlehash.t Tue Oct 18 14:15:15 2016 -0500 @@ -3,11 +3,11 @@ Test wire protocol unbundle with hashed heads (capability: unbundlehash) $ cat << EOF >> $HGRCPATH - > [experimental] + > [devel] > # This tests is intended for bundle1 only. > # bundle2 carries the head information inside the bundle itself and > # always uses 'force' as the heads value. - > bundle2-exp = False + > legacy.exchange = bundle1 > EOF Create a remote repository.
--- a/tests/test-up-local-change.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-up-local-change.t Tue Oct 18 14:15:15 2016 -0500 @@ -67,6 +67,10 @@ summary: 2 $ hg --debug up 0 + starting 4 threads for background file closing (?) + searching for copies back to rev 0 + unmatched files in local (from topological common ancestor): + b resolving manifests branchmerge: False, force: False, partial: False ancestor: 1e71731e6fbb, local: 1e71731e6fbb+, remote: c19d34741b0a @@ -222,4 +226,20 @@ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg st +test updating backwards through a rename + + $ hg mv a b + $ hg ci -m b + $ echo b > b + $ hg up -q 0 + $ hg st + M a + $ hg diff --nodates + diff -r cb9a9f314b8b a + --- a/a + +++ b/a + @@ -1,1 +1,1 @@ + -a + +b + $ cd ..
--- a/tests/test-update-branches.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-update-branches.t Tue Oct 18 14:15:15 2016 -0500 @@ -195,7 +195,7 @@ $ norevtest "on closed branch head" clean 6 0 files updated, 0 files merged, 0 files removed, 0 files unresolved no open descendant heads on branch "default", updating to a closed head - (committing will reopen the head, use `hg heads .` to see 1 other heads) + (committing will reopen the head, use 'hg heads .' to see 1 other heads) parent=6 if descendant non-closed branch head exists, and it is only one branch head: @@ -214,7 +214,7 @@ $ norevtest "all descendant branch heads are closed" clean 3 0 files updated, 0 files merged, 0 files removed, 0 files unresolved no open descendant heads on branch "default", updating to a closed head - (committing will reopen the head, use `hg heads .` to see 1 other heads) + (committing will reopen the head, use 'hg heads .' to see 1 other heads) parent=6 Test updating if all branch heads are closed @@ -379,3 +379,14 @@ $ hg log -r '_destupdate()' 2:bd10386d478c 2 (no-eol) + +Test that boolean flags allow --no-flag specification to override [defaults] + $ cat >> $HGRCPATH <<EOF + > [defaults] + > update = --check + > EOF + $ hg co 2 + abort: uncommitted changes + [255] + $ hg co --no-check 2 + 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
--- a/tests/test-update-names.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-update-names.t Tue Oct 18 14:15:15 2016 -0500 @@ -72,3 +72,15 @@ $ cd .. #endif + +Test that warning is printed if cwd is deleted during update + $ hg init r4 && cd r4 + $ mkdir dir + $ cd dir + $ echo a > a + $ echo b > b + $ hg add a b + $ hg ci -m "file and dir" + $ hg up -q null + current directory was removed + (consider changing to repo root: $TESTTMP/r1/r4)
--- a/tests/test-url-rev.t Tue Oct 18 14:13:06 2016 -0500 +++ b/tests/test-url-rev.t Tue Oct 18 14:15:15 2016 -0500 @@ -41,12 +41,12 @@ summary: change a $ cat clone/.hg/hgrc - # example repository config (see "hg help config" for more info) + # example repository config (see 'hg help config' for more info) [paths] default = $TESTTMP/repo#foo (glob) # path aliases to other clones of this repo in URLs or filesystem paths - # (see "hg help config.paths" for more info) + # (see 'hg help config.paths' for more info) # # default-push = ssh://jdoe@example.net/hg/jdoes-fork # my-fork = ssh://jdoe@example.net/hg/jdoes-fork @@ -320,3 +320,12 @@ remote: 1 outgoing $ cd .. + +Test url#rev syntax of local destination path, which should be taken as +a 'url#rev' path + + $ hg clone repo '#foo' + updating to branch default + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ hg root -R '#foo' + $TESTTMP/#foo (glob)