# HG changeset patch # User Matt Mackall # Date 1441145340 18000 # Node ID f77a3f27cea533232dc107a72773e90e2de9549d # Parent 99e8a9ff1f5f049692e8f0b90cfd7a04fd357aae# Parent d0843f7e16b4ef0199613934619b2f3a1154aeba merge with stable diff -r d0843f7e16b4 -r f77a3f27cea5 Makefile --- a/Makefile Tue Sep 01 16:38:33 2015 -0500 +++ b/Makefile Tue Sep 01 17:09:00 2015 -0500 @@ -157,14 +157,12 @@ N=`cd dist && echo mercurial-*.mpkg | sed 's,\.mpkg$$,,'` && hdiutil create -srcfolder dist/$$N.mpkg/ -scrub -volname "$$N" -ov packages/osx/$$N.dmg rm -rf dist/mercurial-*.mpkg -debian-jessie: - mkdir -p packages/debian-jessie - contrib/builddeb - mv debbuild/*.deb packages/debian-jessie - rm -rf debbuild +deb: + mkdir -p packages/debian-unknown + contrib/builddeb --release unknown docker-debian-jessie: - mkdir -p packages/debian/jessie + mkdir -p packages/debian-jessie contrib/dockerdeb jessie fedora20: diff -r d0843f7e16b4 -r f77a3f27cea5 contrib/builddeb --- a/contrib/builddeb Tue Sep 01 16:38:33 2015 -0500 +++ b/contrib/builddeb Tue Sep 01 17:09:00 2015 -0500 @@ -7,9 +7,14 @@ . $(dirname $0)/packagelib.sh BUILD=1 -DEBBUILDDIR="$PWD/debbuild" +DEBVERSION=jessie while [ "$1" ]; do case "$1" in + --release ) + shift + DEBVERSION="$1" + shift + ;; --prepare ) shift BUILD= @@ -26,6 +31,8 @@ esac done +DEBBUILDDIR=${OUTPUTDIR:="$PWD/debbuild"} + set -u rm -rf $DEBBUILDDIR @@ -49,6 +56,7 @@ mv $control.tmp $control if [ "$BUILD" ]; then + make PREFIX=$DEBBUILDDIR/usr install dpkg-deb --build $DEBBUILDDIR mv $DEBBUILDDIR.deb $DEBBUILDDIR/mercurial-$version-$release.deb if [ $? = 0 ]; then diff -r d0843f7e16b4 -r f77a3f27cea5 contrib/dockerdeb --- a/contrib/dockerdeb Tue Sep 01 16:38:33 2015 -0500 +++ b/contrib/dockerdeb Tue Sep 01 17:09:00 2015 -0500 @@ -8,13 +8,14 @@ checkdocker +DEBPLATFORM="$1" PLATFORM="debian-$1" shift # extra params are passed to build process initcontainer $PLATFORM -DEBBUILDDIR=$ROOTDIR/packages/$PLATFORM -contrib/builddeb --debbuilddir $DEBBUILDDIR/staged --prepare +DEBBUILDDIR=${OUTPUTDIR:=$ROOTDIR/packages/$PLATFORM} +OUTPUTDIR=$DEBBUILDDIR/staged contrib/builddeb --release $DEBPLATFORM --prepare DSHARED=/mnt/shared/ if [ $(uname) = "Darwin" ] ; then diff -r d0843f7e16b4 -r f77a3f27cea5 contrib/import-checker.py --- a/contrib/import-checker.py Tue Sep 01 16:38:33 2015 -0500 +++ b/contrib/import-checker.py Tue Sep 01 17:09:00 2015 -0500 @@ -164,7 +164,7 @@ for m in ['msvcrt', '_winreg']: yield m # These get missed too - for m in 'ctypes', 'email': + for m in 'ctypes', 'email', 'multiprocessing': yield m yield 'builtins' # python3 only for m in 'fcntl', 'grp', 'pwd', 'termios': # Unix only diff -r d0843f7e16b4 -r f77a3f27cea5 contrib/revsetbenchmarks.py --- a/contrib/revsetbenchmarks.py Tue Sep 01 16:38:33 2015 -0500 +++ b/contrib/revsetbenchmarks.py Tue Sep 01 17:09:00 2015 -0500 @@ -33,6 +33,8 @@ """update the repo to a revision""" try: check_call(['hg', 'update', '--quiet', '--check', str(rev)]) + check_output(['make', 'local'], + stderr=None) # suppress output except for error/warning except CalledProcessError as exc: print >> sys.stderr, 'update to revision %s failed, aborting' % rev sys.exit(exc.returncode) diff -r d0843f7e16b4 -r f77a3f27cea5 contrib/showstack.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/contrib/showstack.py Tue Sep 01 17:09:00 2015 -0500 @@ -0,0 +1,17 @@ +# showstack.py - extension to dump a Python stack trace on signal +# +# binds to both SIGQUIT (Ctrl-\) and SIGINFO (Ctrl-T on BSDs) + +import sys, signal, traceback + +def sigshow(*args): + sys.stderr.write("\n") + traceback.print_stack(args[1], limit=10, file=sys.stderr) + sys.stderr.write("----\n") + +def extsetup(ui): + signal.signal(signal.SIGQUIT, sigshow) + try: + signal.signal(signal.SIGINFO, sigshow) + except AttributeError: + pass diff -r d0843f7e16b4 -r f77a3f27cea5 contrib/vim/patchreview.txt --- a/contrib/vim/patchreview.txt Tue Sep 01 16:38:33 2015 -0500 +++ b/contrib/vim/patchreview.txt Tue Sep 01 17:09:00 2015 -0500 @@ -30,7 +30,7 @@ software development projects. This plugin provides that missing functionality. -It also improves on |:diffpatch|'s behaviour of creating the patched files in +It also improves on |:diffpatch|'s behavior of creating the patched files in the same directory as original file which can lead to project workspace pollution. diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/convert/__init__.py --- a/hgext/convert/__init__.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/convert/__init__.py Tue Sep 01 17:09:00 2015 -0500 @@ -316,6 +316,9 @@ ``convert.git.remoteprefix`` as a prefix followed by a /. The default is 'remote'. + :convert.git.skipsubmodules: does not convert root level .gitmodules files + or files with 160000 mode indicating a submodule. Default is False. + Perforce Source ############### diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/convert/common.py --- a/hgext/convert/common.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/convert/common.py Tue Sep 01 17:09:00 2015 -0500 @@ -82,6 +82,13 @@ def after(self): pass + def targetfilebelongstosource(self, targetfilename): + """Returns true if the given targetfile belongs to the source repo. This + is useful when only a subdirectory of the target belongs to the source + repo.""" + # For normal full repo converts, this is always True. + return True + def setrevmap(self, revmap): """set the map of already-converted revisions""" pass diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/convert/convcmd.py --- a/hgext/convert/convcmd.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/convert/convcmd.py Tue Sep 01 17:09:00 2015 -0500 @@ -120,6 +120,9 @@ item=file, total=self.filecount) return self.source.getfile(file, rev) + def targetfilebelongstosource(self, targetfilename): + return self.source.targetfilebelongstosource(targetfilename) + def lookuprev(self, rev): return self.source.lookuprev(rev) diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/convert/filemap.py --- a/hgext/convert/filemap.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/convert/filemap.py Tue Sep 01 17:09:00 2015 -0500 @@ -42,6 +42,7 @@ self.include = {} self.exclude = {} self.rename = {} + self.targetprefixes = None if path: if self.parse(path): raise util.Abort(_('errors in filemap')) @@ -100,6 +101,30 @@ pass return '', name, '' + def istargetfile(self, filename): + """Return true if the given target filename is covered as a destination + of the filemap. This is useful for identifying what parts of the target + repo belong to the source repo and what parts don't.""" + if self.targetprefixes is None: + self.targetprefixes = set() + for before, after in self.rename.iteritems(): + self.targetprefixes.add(after) + + # If "." is a target, then all target files are considered from the + # source. + if not self.targetprefixes or '.' in self.targetprefixes: + return True + + filename = normalize(filename) + for pre, suf in rpairs(filename): + # This check is imperfect since it doesn't account for the + # include/exclude list, but it should work in filemaps that don't + # apply include/exclude to the same source directories they are + # renaming. + if pre in self.targetprefixes: + return True + return False + def __call__(self, name): if self.include: inc = self.lookup(name, self.include)[0] @@ -410,6 +435,9 @@ return files, ncopies, ncleanp2 + def targetfilebelongstosource(self, targetfilename): + return self.filemapper.istargetfile(targetfilename) + def getfile(self, name, rev): realname, realrev = rev return self.base.getfile(realname, realrev) diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/convert/git.py --- a/hgext/convert/git.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/convert/git.py Tue Sep 01 17:09:00 2015 -0500 @@ -224,6 +224,8 @@ lcount = len(difftree) i = 0 + skipsubmodules = self.ui.configbool('convert', 'git.skipsubmodules', + False) def add(entry, f, isdest): seen.add(f) h = entry[3] @@ -232,6 +234,9 @@ renamesource = (not isdest and entry[4][0] == 'R') if f == '.gitmodules': + if skipsubmodules: + return + subexists[0] = True if entry[4] == 'D' or renamesource: subdeleted[0] = True @@ -239,7 +244,8 @@ else: changes.append(('.hgsub', '')) elif entry[1] == '160000' or entry[0] == ':160000': - subexists[0] = True + if not skipsubmodules: + subexists[0] = True else: if renamesource: h = hex(nullid) @@ -377,28 +383,31 @@ def getbookmarks(self): bookmarks = {} - # Interesting references in git are prefixed - prefix = 'refs/heads/' - prefixlen = len(prefix) + # Handle local and remote branches + remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote') + reftypes = [ + # (git prefix, hg prefix) + ('refs/remotes/origin/', remoteprefix + '/'), + ('refs/heads/', '') + ] - # factor two commands - remoteprefix = self.ui.config('convert', 'git.remoteprefix', 'remote') - gitcmd = { remoteprefix + '/': 'git ls-remote --heads origin', - '': 'git show-ref'} + exclude = set([ + 'refs/remotes/origin/HEAD', + ]) - # Origin heads - for reftype in gitcmd: - try: - fh = self.gitopen(gitcmd[reftype], err=subprocess.PIPE) - for line in fh: - line = line.strip() - rev, name = line.split(None, 1) - if not name.startswith(prefix): + try: + fh = self.gitopen('git show-ref', err=subprocess.PIPE) + for line in fh: + line = line.strip() + rev, name = line.split(None, 1) + # Process each type of branch + for gitprefix, hgprefix in reftypes: + if not name.startswith(gitprefix) or name in exclude: continue - name = '%s%s' % (reftype, name[prefixlen:]) + name = '%s%s' % (hgprefix, name[len(gitprefix):]) bookmarks[name] = rev - except Exception: - pass + except Exception: + pass return bookmarks diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/convert/hg.py --- a/hgext/convert/hg.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/convert/hg.py Tue Sep 01 17:09:00 2015 -0500 @@ -23,6 +23,7 @@ from mercurial.node import bin, hex, nullid from mercurial import hg, util, context, bookmarks, error, scmutil, exchange from mercurial import phases +from mercurial import merge as mergemod from common import NoRepo, commit, converter_source, converter_sink, mapfile @@ -176,14 +177,58 @@ return fp.getvalue() + def _calculatemergedfiles(self, source, p1ctx, p2ctx): + """Calculates the files from p2 that we need to pull in when merging p1 + and p2, given that the merge is coming from the given source. + + This prevents us from losing files that only exist in the target p2 and + that don't come from the source repo (like if you're merging multiple + repositories together). + """ + anc = [p1ctx.ancestor(p2ctx)] + # Calculate what files are coming from p2 + actions, diverge, rename = mergemod.calculateupdates( + self.repo, p1ctx, p2ctx, anc, + True, # branchmerge + True, # force + False, # partial + False, # acceptremote + False, # followcopies + ) + + for file, (action, info, msg) in actions.iteritems(): + if source.targetfilebelongstosource(file): + # If the file belongs to the source repo, ignore the p2 + # since it will be covered by the existing fileset. + continue + + # If the file requires actual merging, abort. We don't have enough + # context to resolve merges correctly. + if action in ['m', 'dm', 'cd', 'dc']: + raise util.Abort(_("unable to convert merge commit " + "since target parents do not merge cleanly (file " + "%s, parents %s and %s)") % (file, p1ctx, + p2ctx)) + elif action == 'k': + # 'keep' means nothing changed from p1 + continue + else: + # Any other change means we want to take the p2 version + yield file + def putcommit(self, files, copies, parents, commit, source, revmap, full, cleanp2): files = dict(files) def getfilectx(repo, memctx, f): - if p2ctx and f in cleanp2 and f not in copies: + if p2ctx and f in p2files and f not in copies: self.ui.debug('reusing %s from p2\n' % f) - return p2ctx[f] + try: + return p2ctx[f] + except error.ManifestLookupError: + # If the file doesn't exist in p2, then we're syncing a + # delete, so just return None. + return None try: v = files[f] except KeyError: @@ -255,6 +300,7 @@ while parents: p1 = p2 p2 = parents.pop(0) + p1ctx = self.repo[p1] p2ctx = None if p2 != nullid: p2ctx = self.repo[p2] @@ -262,6 +308,13 @@ if full: fileset.update(self.repo[p1]) fileset.update(self.repo[p2]) + + if p2ctx: + p2files = set(cleanp2) + for file in self._calculatemergedfiles(source, p1ctx, p2ctx): + p2files.add(file) + fileset.add(file) + ctx = context.memctx(self.repo, (p1, p2), text, fileset, getfilectx, commit.author, commit.date, extra) diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/eol.py --- a/hgext/eol.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/eol.py Tue Sep 01 17:09:00 2015 -0500 @@ -23,7 +23,7 @@ ``native`` is an alias for checking out in the platform's default line ending: ``LF`` on Unix (including Mac OS X) and ``CRLF`` on Windows. Note that ``BIN`` (do nothing to line endings) is Mercurial's -default behaviour; it is only needed if you need to override a later, +default behavior; it is only needed if you need to override a later, more general pattern. The optional ``[repository]`` section specifies the line endings to diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/highlight/highlight.py --- a/hgext/highlight/highlight.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/highlight/highlight.py Tue Sep 01 17:09:00 2015 -0500 @@ -49,7 +49,12 @@ try: lexer = guess_lexer(text[:1024], stripnl=False) except (ClassNotFound, ValueError): - lexer = TextLexer(stripnl=False) + # Don't highlight unknown files + return + + # Don't highlight text files + if isinstance(lexer, TextLexer): + return formatter = HtmlFormatter(nowrap=True, style=style) diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/histedit.py --- a/hgext/histedit.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/histedit.py Tue Sep 01 17:09:00 2015 -0500 @@ -38,7 +38,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # In this file, lines beginning with ``#`` are ignored. You must specify a rule @@ -60,7 +60,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # At which point you close the editor and ``histedit`` starts working. When you @@ -144,7 +144,7 @@ repo, you can add a ``--force`` option. Histedit rule lines are truncated to 80 characters by default. You -can customise this behaviour by setting a different length in your +can customise this behavior by setting a different length in your configuration file:: [histedit] @@ -198,7 +198,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history -# m, mess = edit message without changing commit content +# m, mess = edit commit message without changing commit content # """) @@ -675,9 +675,9 @@ destination repository. If URL of the destination is omitted, the 'default-push' (or 'default') path will be used. - For safety, this command is aborted, also if there are ambiguous - outgoing revisions which may confuse users: for example, there are - multiple branches containing outgoing revisions. + For safety, this command is also aborted if there are ambiguous + outgoing revisions which may confuse users: for example, if there + are multiple branches containing outgoing revisions. Use "min(outgoing() and ::.)" or similar revset specification instead of --outgoing to specify edit target revision exactly in @@ -778,7 +778,7 @@ return elif goal == 'abort': state.read() - mapping, tmpnodes, leafs, _ntm = processreplacement(state) + tmpnodes, leafs = newnodestoabort(state) ui.debug('restore wc to old parent %s\n' % node.short(state.topmost)) # Recover our old commits if necessary @@ -791,13 +791,9 @@ os.remove(backupfile) # check whether we should update away - parentnodes = [c.node() for c in repo[None].parents()] - for n in leafs | set([state.parentctxnode]): - if n in parentnodes: - hg.clean(repo, state.topmost) - break - else: - pass + if repo.unfiltered().revs('parents() and (%n or %ln::)', + state.parentctxnode, leafs | tmpnodes): + hg.clean(repo, state.topmost) cleanupnode(ui, repo, 'created', tmpnodes) cleanupnode(ui, repo, 'temp', leafs) state.clear() @@ -1009,6 +1005,25 @@ hint=_('do you want to use the drop action?')) return parsed +def newnodestoabort(state): + """process the list of replacements to return + + 1) the list of final node + 2) the list of temporary node + + This meant to be used on abort as less data are required in this case. + """ + replacements = state.replacements + allsuccs = set() + replaced = set() + for rep in replacements: + allsuccs.update(rep[1]) + replaced.add(rep[0]) + newnodes = allsuccs - replaced + tmpnodes = allsuccs & replaced + return newnodes, tmpnodes + + def processreplacement(state): """process the list of replacements to return @@ -1019,15 +1034,15 @@ allsuccs = set() replaced = set() fullmapping = {} - # initialise basic set - # fullmapping record all operation recorded in replacement + # initialize basic set + # fullmapping records all operations recorded in replacement for rep in replacements: allsuccs.update(rep[1]) replaced.add(rep[0]) fullmapping.setdefault(rep[0], set()).update(rep[1]) new = allsuccs - replaced tmpnodes = allsuccs & replaced - # Reduce content fullmapping into direct relation between original nodes + # Reduce content fullmapping into direct relation between original nodes # and final node created during history edition # Dropped changeset are replaced by an empty list toproceed = set(fullmapping) @@ -1113,6 +1128,10 @@ lock = None try: lock = repo.lock() + # do not let filtering get in the way of the cleanse + # we should probably get ride of obsolescence marker created during the + # histedit, but we currently do not have such information. + repo = repo.unfiltered() # Find all node that need to be stripped # (we hg %lr instead of %ln to silently ignore unknown item nm = repo.changelog.nodemap diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/mq.py --- a/hgext/mq.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/mq.py Tue Sep 01 17:09:00 2015 -0500 @@ -28,7 +28,7 @@ By default, mq will automatically use git patches when required to avoid losing file mode changes, copy records, binary files or empty -files creations or deletions. This behaviour can be configured with:: +files creations or deletions. This behavior can be configured with:: [mq] git = auto/keep/yes/no diff -r d0843f7e16b4 -r f77a3f27cea5 hgext/progress.py --- a/hgext/progress.py Tue Sep 01 16:38:33 2015 -0500 +++ b/hgext/progress.py Tue Sep 01 17:09:00 2015 -0500 @@ -10,3 +10,8 @@ This extension has been merged into core, you can remove it from your config. See hg help config.progress for configuration options. """ +# Note for extension authors: ONLY specify testedwith = 'internal' for +# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should +# be specifying the version(s) of Mercurial they are tested with, or +# leave the attribute unspecified. +testedwith = 'internal' diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/ancestor.py --- a/mercurial/ancestor.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/ancestor.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,12 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import collections import heapq -from node import nullrev + +from .node import nullrev def commonancestorsheads(pfunc, *nodes): """Returns a set with the heads of all common ancestors of all nodes, diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/archival.py --- a/mercurial/archival.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/archival.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,14 +5,27 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import match as matchmod -import cmdutil -import scmutil, util, encoding -import cStringIO, os, tarfile, time, zipfile -import zlib, gzip +from __future__ import absolute_import + +import cStringIO +import gzip +import os import struct -import error +import tarfile +import time +import zipfile +import zlib + +from .i18n import _ + +from . import ( + cmdutil, + encoding, + error, + match as matchmod, + scmutil, + util, +) # from unzip source code: _UNX_IFREG = 0x8000 diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/base85.c --- a/mercurial/base85.c Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/base85.c Tue Sep 01 17:09:00 2015 -0500 @@ -21,7 +21,7 @@ static void b85prep(void) { - int i; + unsigned i; memset(b85dec, 0, sizeof(b85dec)); for (i = 0; i < sizeof(b85chars); i++) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/bookmarks.py --- a/mercurial/bookmarks.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/bookmarks.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,11 +5,22 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + +import errno import os -from mercurial.i18n import _ -from mercurial.node import hex, bin -from mercurial import encoding, util, obsolete, lock as lockmod -import errno + +from .i18n import _ +from .node import ( + bin, + hex, +) +from . import ( + encoding, + lock as lockmod, + obsolete, + util, +) class bmstore(dict): """Storage for bookmarks. @@ -79,6 +90,11 @@ can be copied back on rollback. ''' repo = self._repo + if (repo.ui.configbool('devel', 'all-warnings') + or repo.ui.configbool('devel', 'check-locks')): + l = repo._wlockref and repo._wlockref() + if l is None or not l.held: + repo.ui.develwarn('bookmarks write with no wlock') self._writerepo(repo) repo.invalidatevolatilesets() diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/branchmap.py --- a/mercurial/branchmap.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/branchmap.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,13 +5,28 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import bin, hex, nullid, nullrev -import encoding -import scmutil -import util +from __future__ import absolute_import + +import array +import struct import time -from array import array -from struct import calcsize, pack, unpack + +from .node import ( + bin, + hex, + nullid, + nullrev, +) +from . import ( + encoding, + scmutil, + util, +) + +array = array.array +calcsize = struct.calcsize +pack = struct.pack +unpack = struct.unpack def _filename(repo): """name of a branchcache file for a given repo or repoview""" diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/bundle2.py --- a/mercurial/bundle2.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/bundle2.py Tue Sep 01 17:09:00 2015 -0500 @@ -145,19 +145,25 @@ preserve. """ +from __future__ import absolute_import + import errno -import sys -import util -import struct -import urllib +import re import string -import obsolete -import pushkey -import url -import re +import struct +import sys +import urllib -import changegroup, error, tags -from i18n import _ +from .i18n import _ +from . import ( + changegroup, + error, + obsolete, + pushkey, + tags, + url, + util, +) _pack = struct.pack _unpack = struct.unpack @@ -1233,7 +1239,7 @@ # we need to make sure we trigger the creation of a transaction object used # for the whole processing scope. op.gettransaction() - import exchange + from . import exchange cg = exchange.readbundle(op.repo.ui, real_part, raw_url) if not isinstance(cg, changegroup.cg1unpacker): raise util.Abort(_('%s: not a bundle version 1.0') % diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/bundlerepo.py Tue Sep 01 17:09:00 2015 -0500 @@ -11,12 +11,33 @@ were part of the actual repository. """ -from node import nullid -from i18n import _ -import os, tempfile, shutil -import changegroup, util, mdiff, discovery, cmdutil, scmutil, exchange -import localrepo, changelog, manifest, filelog, revlog, error, phases, bundle2 -import pathutil +from __future__ import absolute_import + +import os +import shutil +import tempfile + +from .i18n import _ +from .node import nullid + +from . import ( + bundle2, + changegroup, + changelog, + cmdutil, + discovery, + error, + exchange, + filelog, + localrepo, + manifest, + mdiff, + pathutil, + phases, + revlog, + scmutil, + util, +) class bundlerevlog(revlog.revlog): def __init__(self, opener, indexfile, bundle, linkmapper): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/changegroup.py --- a/mercurial/changegroup.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/changegroup.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,12 +5,32 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + +import bz2 +import os +import struct +import tempfile import weakref -from i18n import _ -from node import nullrev, nullid, hex, short -import mdiff, util, dagutil -import struct, os, bz2, zlib, tempfile -import discovery, error, phases, branchmap +import zlib + +from .i18n import _ +from .node import ( + hex, + nullid, + nullrev, + short, +) + +from . import ( + branchmap, + dagutil, + discovery, + error, + mdiff, + phases, + util, +) _CHANGEGROUPV1_DELTA_HEADER = "20s20s20s20s" _CHANGEGROUPV2_DELTA_HEADER = "20s20s20s20s20s" @@ -103,7 +123,7 @@ cleanup = filename if bundletype == "HG20": - import bundle2 + from . import bundle2 bundle = bundle2.bundle20(ui) part = bundle.newpart('changegroup', data=cg.getchunks()) part.addparam('version', cg.version) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/changelog.py --- a/mercurial/changelog.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/changelog.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,21 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import bin, hex, nullid -from i18n import _ -import util, error, revlog, encoding +from __future__ import absolute_import + +from .i18n import _ +from .node import ( + bin, + hex, + nullid, +) + +from . import ( + encoding, + error, + revlog, + util, +) _defaultextra = {'branch': 'default'} @@ -172,6 +184,9 @@ self.rev(self.node(0)) return self._nodecache + def reachableroots(self, minroot, heads, roots, includepath=False): + return self.index.reachableroots2(minroot, heads, roots, includepath) + def headrevs(self): if self.filteredrevs: try: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/cmdutil.py --- a/mercurial/cmdutil.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/cmdutil.py Tue Sep 01 17:09:00 2015 -0500 @@ -10,7 +10,7 @@ import os, sys, errno, re, tempfile, cStringIO, shutil import util, scmutil, templater, patch, error, templatekw, revlog, copies import match as matchmod -import context, repair, graphmod, revset, phases, obsolete, pathutil +import repair, graphmod, revset, phases, obsolete, pathutil import changelog import bookmarks import encoding @@ -848,6 +848,8 @@ :updatefunc: a function that update a repo to a given node updatefunc(, ) """ + # avoid cycle context -> subrepo -> cmdutil + import context tmpname, message, user, date, branch, nodeid, p1, p2 = \ patch.extract(ui, hunk) @@ -1412,15 +1414,32 @@ self.cache = {} + # find correct templates for current mode + tmplmodes = [ + (True, None), + (self.ui.verbose, 'verbose'), + (self.ui.quiet, 'quiet'), + (self.ui.debugflag, 'debug'), + ] + + self._parts = {'header': '', 'footer': '', 'changeset': 'changeset'} + for mode, postfix in tmplmodes: + for t in self._parts: + cur = t + if postfix: + cur += "_" + postfix + if mode and cur in self.t: + self._parts[t] = cur + def _show(self, ctx, copies, matchfn, props): '''show a single changeset or file revision''' showlist = templatekw.showlist - # showparents() behaviour depends on ui trace level which - # causes unexpected behaviours at templating level and makes + # showparents() behavior depends on ui trace level which + # causes unexpected behaviors at templating level and makes # it harder to extract it in a standalone function. Its - # behaviour cannot be changed so leave it here for now. + # behavior cannot be changed so leave it here for now. def showparents(**args): ctx = args['ctx'] parents = [[('rev', p.rev()), @@ -1438,27 +1457,10 @@ props['revcache'] = {'copies': copies} props['cache'] = self.cache - # find correct templates for current mode - - tmplmodes = [ - (True, None), - (self.ui.verbose, 'verbose'), - (self.ui.quiet, 'quiet'), - (self.ui.debugflag, 'debug'), - ] - - types = {'header': '', 'footer':'', 'changeset': 'changeset'} - for mode, postfix in tmplmodes: - for type in types: - cur = postfix and ('%s_%s' % (type, postfix)) or type - if mode and cur in self.t: - types[type] = cur - try: - # write header - if types['header']: - h = templater.stringify(self.t(types['header'], **props)) + if self._parts['header']: + h = templater.stringify(self.t(self._parts['header'], **props)) if self.buffered: self.header[ctx.rev()] = h else: @@ -1467,15 +1469,14 @@ self.ui.write(h) # write changeset metadata, then patch if requested - key = types['changeset'] + key = self._parts['changeset'] self.ui.write(templater.stringify(self.t(key, **props))) self.showpatch(ctx.node(), matchfn) - if types['footer']: + if self._parts['footer']: if not self.footer: - self.footer = templater.stringify(self.t(types['footer'], - **props)) - + self.footer = templater.stringify( + self.t(self._parts['footer'], **props)) except KeyError as inst: msg = _("%s: no key named '%s'") raise util.Abort(msg % (self.t.mapfile, inst.args[0])) @@ -1928,7 +1929,7 @@ followfirst = 1 else: followfirst = 0 - # --follow with FILE behaviour depends on revs... + # --follow with FILE behavior depends on revs... it = iter(revs) startrev = it.next() followdescendants = startrev < next(it, startrev) @@ -2049,7 +2050,7 @@ return expr, filematcher def _logrevs(repo, opts): - # Default --rev value depends on --follow but --follow behaviour + # Default --rev value depends on --follow but --follow behavior # depends on revisions resolved from --rev... follow = opts.get('follow') or opts.get('follow_first') if opts.get('rev'): @@ -2464,6 +2465,9 @@ return commitfunc(ui, repo, message, matcher, opts) def amend(ui, repo, commitfunc, old, extra, pats, opts): + # avoid cycle context -> subrepo -> cmdutil + import context + # amend will reuse the existing user if not specified, but the obsolete # marker creation requires that the current user's name is specified. if obsolete.isenabled(repo, obsolete.createmarkersopt): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/commands.py --- a/mercurial/commands.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/commands.py Tue Sep 01 17:09:00 2015 -0500 @@ -13,7 +13,7 @@ import hg, scmutil, util, revlog, copies, error, bookmarks import patch, help, encoding, templatekw, discovery import archival, changegroup, cmdutil, hbisect -import sshserver, hgweb, commandserver +import sshserver, hgweb import extensions from hgweb import server as hgweb_server import merge as mergemod @@ -2700,9 +2700,12 @@ pa.distance(pb), rel)) @command('debugrebuilddirstate|debugrebuildstate', - [('r', 'rev', '', _('revision to rebuild to'), _('REV'))], + [('r', 'rev', '', _('revision to rebuild to'), _('REV')), + ('', 'minimal', None, _('only rebuild files that are inconsistent with ' + 'the working copy parent')), + ], _('[-r REV]')) -def debugrebuilddirstate(ui, repo, rev): +def debugrebuilddirstate(ui, repo, rev, **opts): """rebuild the dirstate as it would look like for the given revision If no revision is specified the first current parent will be used. @@ -2711,13 +2714,33 @@ The actual working directory content or existing dirstate information such as adds or removes is not considered. + ``minimal`` will only rebuild the dirstate status for files that claim to be + tracked but are not in the parent manifest, or that exist in the parent + manifest but are not in the dirstate. It will not change adds, removes, or + modified files that are in the working copy parent. + One use of this command is to make the next :hg:`status` invocation check the actual file content. """ ctx = scmutil.revsingle(repo, rev) wlock = repo.wlock() try: - repo.dirstate.rebuild(ctx.node(), ctx.manifest()) + dirstate = repo.dirstate + + # See command doc for what minimal does. + if opts.get('minimal'): + dirstatefiles = set(dirstate) + ctxfiles = set(ctx.manifest().keys()) + for file in (dirstatefiles | ctxfiles): + indirstate = file in dirstatefiles + inctx = file in ctxfiles + + if indirstate and not inctx and dirstate[file] != 'a': + dirstate.drop(file) + elif inctx and not indirstate: + dirstate.normallookup(file) + else: + dirstate.rebuild(ctx.node(), ctx.manifest()) finally: wlock.release() @@ -2933,7 +2956,7 @@ expansion. """ if ui.verbose: - tree = revset.parse(expr) + tree = revset.parse(expr, lookup=repo.__contains__) ui.note(revset.prettyformat(tree), "\n") newtree = revset.findaliases(ui, tree) if newtree != tree: @@ -2945,7 +2968,7 @@ if opts["optimize"]: weight, optimizedtree = revset.optimize(newtree, True) ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n") - func = revset.match(ui, expr) + func = revset.match(ui, expr, repo) revs = func(repo) if ui.verbose: ui.note("* set:\n", revset.prettyformatset(revs), "\n") @@ -5251,18 +5274,14 @@ # this lets simultaneous -r, -b options continue working opts.setdefault('rev', []).append("null") - dest = ui.expandpath(dest or 'default-push', dest or 'default') - dest, branches = hg.parseurl(dest, opts.get('branch')) + path = ui.paths.getpath(dest, default='default') + if not path: + raise util.Abort(_('default repository not configured!'), + hint=_('see the "path" section in "hg help config"')) + dest, branches = path.pushloc, (path.branch, opts.get('branch') or []) ui.status(_('pushing to %s\n') % util.hidepassword(dest)) revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev')) - try: - other = hg.peer(repo, opts, dest) - except error.RepoError: - if dest == "default-push": - raise util.Abort(_("default repository not configured!"), - hint=_('see the "path" section in "hg help config"')) - else: - raise + other = hg.peer(repo, opts, dest) if revs: revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)] @@ -5713,6 +5732,7 @@ s.serve_forever() if opts["cmdserver"]: + import commandserver service = commandserver.createservice(ui, repo, opts) return cmdutil.service(opts, initfn=service.init, runfn=service.run) @@ -6248,7 +6268,7 @@ raise util.Abort(_("tag '%s' is not a global tag") % n) else: raise util.Abort(_("tag '%s' is not a local tag") % n) - rev_ = nullid + rev_ = 'null' if not message: # we don't translate commit messages message = 'Removed tag %s' % ', '.join(names) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/config.py --- a/mercurial/config.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/config.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import error, util -import os, errno +from __future__ import absolute_import + +import errno +import os + +from .i18n import _ +from . import ( + error, + util, +) class config(object): def __init__(self, data=None, includepaths=[]): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/copies.py --- a/mercurial/copies.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/copies.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,15 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import util, pathutil +from __future__ import absolute_import + import heapq +from . import ( + pathutil, + util, +) + def _findlimit(repo, a, b): """ Find the last revision that needs to be checked to ensure that a full @@ -185,6 +191,9 @@ return cm def _backwardrenames(a, b): + if a._repo.ui.configbool('experimental', 'disablecopytrace'): + return {} + # Even though we're not taking copies into account, 1:n rename situations # can still exist (e.g. hg cp a b; hg mv a c). In those cases we # arbitrarily pick one of the renames. @@ -258,6 +267,12 @@ if c2.node() is None and c1.node() == repo.dirstate.p1(): return repo.dirstate.copies(), {}, {}, {} + # Copy trace disabling is explicitly below the node == p1 logic above + # because the logic above is required for a simple copy to be kept across a + # rebase. + if repo.ui.configbool('experimental', 'disablecopytrace'): + return {}, {}, {}, {} + limit = _findlimit(repo, c1.rev(), c2.rev()) if limit is None: # no common ancestor, no copies @@ -507,7 +522,12 @@ copies between fromrev and rev. ''' exclude = {} - if skiprev is not None: + if (skiprev is not None and + not repo.ui.configbool('experimental', 'disablecopytrace')): + # disablecopytrace skips this line, but not the entire function because + # the line below is O(size of the repo) during a rebase, while the rest + # of the function is much faster (and is required for carrying copy + # metadata across the rebase anyway). exclude = pathcopies(repo[fromrev], repo[skiprev]) for dst, src in pathcopies(repo[fromrev], repo[rev]).iteritems(): # copies.pathcopies returns backward renames, so dst might not diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/crecord.py --- a/mercurial/crecord.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/crecord.py Tue Sep 01 17:09:00 2015 -0500 @@ -8,11 +8,23 @@ # This code is based on the Mark Edgington's crecord extension. # (Itself based on Bryan O'Sullivan's record extension.) -from i18n import _ -import patch as patchmod -import util, encoding +from __future__ import absolute_import -import os, re, sys, struct, signal, tempfile, locale, cStringIO +import cStringIO +import locale +import os +import re +import signal +import struct +import sys +import tempfile + +from .i18n import _ +from . import ( + encoding, + patch as patchmod, + util, +) # This is required for ncurses to display non-ASCII characters in default user # locale encoding correctly. --immerrr @@ -21,7 +33,8 @@ # os.name is one of: 'posix', 'nt', 'dos', 'os2', 'mac', or 'ce' if os.name == 'posix': import curses - import fcntl, termios + import fcntl + import termios else: # I have no idea if wcurses works with crecord... try: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/dagparser.py --- a/mercurial/dagparser.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/dagparser.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,13 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import re, string -import util -from i18n import _ +from __future__ import absolute_import + +import re +import string + +from .i18n import _ +from . import util def parsedag(desc): '''parses a DAG from a concise textual description; generates events diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/dagutil.py --- a/mercurial/dagutil.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/dagutil.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,9 +6,10 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullrev -from i18n import _ +from __future__ import absolute_import +from .i18n import _ +from .node import nullrev class basedag(object): '''generic interface for DAGs diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/demandimport.py --- a/mercurial/demandimport.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/demandimport.py Tue Sep 01 17:09:00 2015 -0500 @@ -24,8 +24,11 @@ b = __import__(a) ''' -import os, sys -from contextlib import contextmanager +from __future__ import absolute_import + +import contextlib +import os +import sys # __builtin__ in Python 2, builtins in Python 3. try: @@ -33,26 +36,21 @@ except ImportError: import builtins +contextmanager = contextlib.contextmanager + _origimport = __import__ nothing = object() -try: - # Python 3 doesn't have relative imports nor level -1. - level = -1 - if sys.version_info[0] >= 3: - level = 0 - _origimport(builtins.__name__, {}, {}, None, level) -except TypeError: # no level argument - def _import(name, globals, locals, fromlist, level): - "call _origimport with no level argument" - return _origimport(name, globals, locals, fromlist) -else: - _import = _origimport +# Python 3 doesn't have relative imports nor level -1. +level = -1 +if sys.version_info[0] >= 3: + level = 0 +_import = _origimport -def _hgextimport(importfunc, name, globals, *args): +def _hgextimport(importfunc, name, globals, *args, **kwargs): try: - return importfunc(name, globals, *args) + return importfunc(name, globals, *args, **kwargs) except ImportError: if not globals: raise @@ -63,7 +61,7 @@ if nameroot != contextroot: raise # retry to import with "hgext_" prefix - return importfunc(hgextname, globals, *args) + return importfunc(hgextname, globals, *args, **kwargs) class _demandmod(object): """module demand-loader and proxy""" @@ -135,15 +133,44 @@ return locals[base] return _demandmod(name, globals, locals, level) else: - if level != -1: - # from . import b,c,d or from .a import b,c,d - return _origimport(name, globals, locals, fromlist, level) + # There is a fromlist. # from a import b,c,d + # from . import b,c,d + # from .a import b,c,d + + # level == -1: relative and absolute attempted (Python 2 only). + # level >= 0: absolute only (Python 2 w/ absolute_import and Python 3). + # The modern Mercurial convention is to use absolute_import everywhere, + # so modern Mercurial code will have level >= 0. + + if level >= 0: + # Mercurial's enforced import style does not use + # "from a import b,c,d" or "from .a import b,c,d" syntax. In + # addition, this appears to be giving errors with some modules + # for unknown reasons. Since we shouldn't be using this syntax + # much, work around the problems. + if name: + return _hgextimport(_origimport, name, globals, locals, + fromlist, level) + + mod = _hgextimport(_origimport, name, globals, locals, level=level) + for x in fromlist: + # Missing symbols mean they weren't defined in the module + # itself which means they are sub-modules. + if getattr(mod, x, nothing) is nothing: + setattr(mod, x, + _demandmod(x, mod.__dict__, locals, level=level)) + + return mod + + # But, we still need to support lazy loading of standard library and 3rd + # party modules. So handle level == -1. mod = _hgextimport(_origimport, name, globals, locals) # recurse down the module chain for comp in name.split('.')[1:]: if getattr(mod, comp, nothing) is nothing: - setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__)) + setattr(mod, comp, + _demandmod(comp, mod.__dict__, mod.__dict__)) mod = getattr(mod, comp) for x in fromlist: # set requested submodules for demand load @@ -152,6 +179,7 @@ return mod ignore = [ + '__future__', '_hashlib', '_xmlplus', 'fcntl', diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/discovery.py --- a/mercurial/discovery.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/discovery.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,10 +5,23 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullid, short -from i18n import _ -import util, setdiscovery, treediscovery, phases, obsolete, bookmarks -import branchmap +from __future__ import absolute_import + +from .i18n import _ +from .node import ( + nullid, + short, +) + +from . import ( + bookmarks, + branchmap, + obsolete, + phases, + setdiscovery, + treediscovery, + util, +) def findcommonincoming(repo, remote, heads=None, force=False): """Return a tuple (common, anyincoming, heads) used to identify the common diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/dispatch.py --- a/mercurial/dispatch.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/dispatch.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,13 +5,37 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import os, sys, atexit, signal, pdb, socket, errno, shlex, time, traceback, re +from __future__ import absolute_import + +import atexit import difflib -import util, commands, hg, fancyopts, extensions, hook, error -import cmdutil, encoding -import ui as uimod -import demandimport +import errno +import os +import pdb +import re +import shlex +import signal +import socket +import sys +import time +import traceback + + +from .i18n import _ + +from . import ( + cmdutil, + commands, + demandimport, + encoding, + error, + extensions, + fancyopts, + hg, + hook, + ui as uimod, + util, +) class request(object): def __init__(self, args, ui=None, repo=None, fin=None, fout=None, @@ -909,7 +933,7 @@ format = 'text' try: - from mercurial import lsprof + from . import lsprof except ImportError: raise util.Abort(_( 'lsprof not available - install from ' @@ -922,7 +946,7 @@ p.disable() if format == 'kcachegrind': - import lsprofcalltree + from . import lsprofcalltree calltree = lsprofcalltree.KCacheGrind(p) calltree.output(fp) else: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/error.py --- a/mercurial/error.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/error.py Tue Sep 01 17:09:00 2015 -0500 @@ -11,6 +11,8 @@ imports. """ +from __future__ import absolute_import + # Do not import anything here, please class HintException(Exception): @@ -32,7 +34,7 @@ # Python 2.6+ complain about the 'message' property being deprecated self.lookupmessage = message if isinstance(name, str) and len(name) == 20: - from node import short + from .node import short name = short(name) RevlogError.__init__(self, '%s@%s: %s' % (index, name, message)) @@ -78,7 +80,7 @@ """Exception raised when a {rev,file}set references an unknown identifier""" def __init__(self, function, symbols): - from i18n import _ + from .i18n import _ ParseError.__init__(self, _("unknown identifier: %s") % function) self.function = function self.symbols = symbols @@ -173,7 +175,7 @@ """ def __init__(self, filename, node, tombstone): - from node import short + from .node import short RevlogError.__init__(self, '%s:%s' % (filename, short(node))) self.tombstone = tombstone diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/exchange.py --- a/mercurial/exchange.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/exchange.py Tue Sep 01 17:09:00 2015 -0500 @@ -571,7 +571,7 @@ @b2partsgenerator('bookmarks') def _pushb2bookmarks(pushop, bundler): - """handle phase push through bundle2""" + """handle bookmark push through bundle2""" if 'bookmarks' in pushop.stepsdone: return b2caps = bundle2.bundle2caps(pushop.remote) @@ -1419,7 +1419,7 @@ op = bundle2.bundleoperation(repo, lambda: tr, captureoutput=captureoutput) try: - r = bundle2.processbundle(repo, cg, op=op) + op = bundle2.processbundle(repo, cg, op=op) finally: r = op.reply if captureoutput and r is not None: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/extensions.py --- a/mercurial/extensions.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/extensions.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,21 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import imp, os -import util, cmdutil, error -from i18n import _, gettext +from __future__ import absolute_import + +import imp +import os + +from .i18n import ( + _, + gettext, +) + +from . import ( + cmdutil, + error, + util, +) _extensions = {} _aftercallbacks = {} diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/fancyopts.py --- a/mercurial/fancyopts.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/fancyopts.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,12 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import getopt -import util -from i18n import _ + +from .i18n import _ +from . import util def gnugetopt(args, options, longoptions): """Parse options mostly like getopt.gnu_getopt. diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/filelog.py --- a/mercurial/filelog.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/filelog.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,8 +5,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import error, mdiff, revlog -import re, struct +from __future__ import absolute_import + +import re +import struct + +from . import ( + error, + mdiff, + revlog, +) _mdre = re.compile('\1\n') def parsemeta(text): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/filemerge.py --- a/mercurial/filemerge.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/filemerge.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,11 +5,25 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import short -from i18n import _ -import util, simplemerge, match, error, templater, templatekw -import os, tempfile, re, filecmp -import tagmerge +from __future__ import absolute_import + +import filecmp +import os +import re +import tempfile + +from .i18n import _ +from .node import short + +from . import ( + error, + match, + simplemerge, + tagmerge, + templatekw, + templater, + util, +) def _toolstr(ui, tool, part, default=""): return ui.config("merge-tools", tool + "." + part, default) @@ -213,15 +227,12 @@ util.copyfile(back, a) # restore from backup and try again return 1 # continue merging -@internaltool('merge', True, - _("merging %s incomplete! " - "(edit conflicts, then use 'hg resolve --mark')\n")) -def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): +def _merge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels, mode): """ Uses the internal non-interactive simple merge algorithm for merging files. It will fail if there are any conflicts and leave markers in the partially merged file. Markers will have two sections, one for each side - of merge.""" + of merge, unless mode equals 'union' which suppresses the markers.""" tool, toolpath, binary, symlink = toolconf if symlink: repo.ui.warn(_('warning: internal :merge cannot merge symlinks ' @@ -233,10 +244,33 @@ ui = repo.ui - r = simplemerge.simplemerge(ui, a, b, c, label=labels) + r = simplemerge.simplemerge(ui, a, b, c, label=labels, mode=mode) return True, r return False, 0 +@internaltool('union', True, + _("merging %s incomplete! " + "(edit conflicts, then use 'hg resolve --mark')\n")) +def _iunion(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): + """ + Uses the internal non-interactive simple merge algorithm for merging + files. It will use both left and right sides for conflict regions. + No markers are inserted.""" + return _merge(repo, mynode, orig, fcd, fco, fca, toolconf, + files, labels, 'union') + +@internaltool('merge', True, + _("merging %s incomplete! " + "(edit conflicts, then use 'hg resolve --mark')\n")) +def _imerge(repo, mynode, orig, fcd, fco, fca, toolconf, files, labels=None): + """ + Uses the internal non-interactive simple merge algorithm for merging + files. It will fail if there are any conflicts and leave markers in + the partially merged file. Markers will have two sections, one for each side + of merge.""" + return _merge(repo, mynode, orig, fcd, fco, fca, toolconf, + files, labels, 'merge') + @internaltool('merge3', True, _("merging %s incomplete! " "(edit conflicts, then use 'hg resolve --mark')\n")) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/fileset.py --- a/mercurial/fileset.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/fileset.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,17 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import re -import parser, error, util, merge -from i18n import _ + +from .i18n import _ +from . import ( + error, + merge, + parser, + util, +) elements = { # token-type: binding-strength, primary, prefix, infix, suffix @@ -410,7 +418,7 @@ # i18n: "subrepo" is a keyword pat = getstring(x, _("subrepo requires a pattern or no arguments")) - import match as matchmod # avoid circular import issues + from . import match as matchmod # avoid circular import issues fast = not matchmod.patkind(pat) if fast: def m(s): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/formatter.py --- a/mercurial/formatter.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/formatter.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,13 +5,23 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import cPickle -from node import hex, short -from i18n import _ -import encoding, util -import templater import os +from .i18n import _ +from .node import ( + hex, + short, +) + +from . import ( + encoding, + templater, + util, +) + class baseformatter(object): def __init__(self, ui, topic, opts): self._ui = ui diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/graphmod.py --- a/mercurial/graphmod.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/graphmod.py Tue Sep 01 17:09:00 2015 -0500 @@ -17,11 +17,16 @@ Data depends on type. """ -from mercurial.node import nullrev -import util +from __future__ import absolute_import import heapq +from .node import nullrev +from . import ( + revset, + util, +) + CHANGESET = 'C' def groupbranchiter(revs, parentsfunc, firstbranch=()): @@ -233,8 +238,6 @@ if not revs: return - cl = repo.changelog - lowestrev = revs.min() gpcache = {} if repo.ui.configbool('experimental', 'graph-group-branches', False): @@ -244,7 +247,8 @@ if firstbranchrevset: firstbranch = repo.revs(firstbranchrevset) parentrevs = repo.changelog.parentrevs - revs = list(groupbranchiter(revs, parentrevs, firstbranch)) + revs = groupbranchiter(revs, parentrevs, firstbranch) + revs = revset.baseset(revs) for rev in revs: ctx = repo[rev] @@ -256,7 +260,7 @@ for mpar in mpars: gp = gpcache.get(mpar) if gp is None: - gp = gpcache[mpar] = grandparent(cl, lowestrev, revs, mpar) + gp = gpcache[mpar] = revset.reachableroots(repo, revs, [mpar]) if not gp: parents.append(mpar) else: @@ -354,24 +358,6 @@ yield (cur, type, data, (col, color), edges) seen = next -def grandparent(cl, lowestrev, roots, head): - """Return all ancestors of head in roots which revision is - greater or equal to lowestrev. - """ - pending = set([head]) - seen = set() - kept = set() - llowestrev = max(nullrev, lowestrev) - while pending: - r = pending.pop() - if r >= llowestrev and r not in seen: - if r in roots: - kept.add(r) - else: - pending.update([p for p in cl.parentrevs(r)]) - seen.add(r) - return sorted(kept) - def asciiedges(type, char, lines, seen, rev, parents): """adds edge info to changelog DAG walk suitable for ascii()""" if rev not in seen: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/hbisect.py --- a/mercurial/hbisect.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/hbisect.py Tue Sep 01 17:09:00 2015 -0500 @@ -8,12 +8,20 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import collections import os -import error -from i18n import _ -from node import short, hex -import util + +from .i18n import _ +from .node import ( + hex, + short, +) +from . import ( + error, + util, +) def bisect(changelog, state): """find the next node (if any) for testing during a bisect search. diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/help/config.txt --- a/mercurial/help/config.txt Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/help/config.txt Tue Sep 01 17:09:00 2015 -0500 @@ -1752,6 +1752,14 @@ Whether to require that inbound pushes be transported over SSL to prevent password sniffing. Default is True. +``refreshinterval`` + How frequently directory listings re-scan the filesystem for new + repositories, in seconds. This is relevant when wildcards are used + to define paths. Depending on how much filesystem traversal is + required, refreshing may negatively impact performance. + + Default is 20. Values less than or equal to 0 always refresh. + ``staticurl`` Base URL to use for static files. If unset, static files (e.g. the hgicon.png favicon) will be served by the CGI script itself. Use diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/help/glossary.txt --- a/mercurial/help/glossary.txt Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/help/glossary.txt Tue Sep 01 17:09:00 2015 -0500 @@ -32,12 +32,12 @@ explicitly with a named branch, but it can also be done locally, using bookmarks or clones and anonymous branches. - Example: "The experimental branch". + Example: "The experimental branch." (Verb) The action of creating a child changeset which results in its parent having more than one child. - Example: "I'm going to branch at X". + Example: "I'm going to branch at X." Branch, anonymous Every time a new child changeset is created from a parent that is not @@ -135,7 +135,7 @@ See 'Changeset, child'. Close changeset - See 'Head, closed branch' + See 'Head, closed branch'. Closed branch See 'Branch, closed'. @@ -144,11 +144,11 @@ (Noun) An entire or partial copy of a repository. The partial clone must be in the form of a revision and its ancestors. - Example: "Is your clone up to date?". + Example: "Is your clone up to date?" (Verb) The process of creating a clone, using :hg:`clone`. - Example: "I'm going to clone the repository". + Example: "I'm going to clone the repository." Closed branch head See 'Head, closed branch'. @@ -398,13 +398,13 @@ Update (Noun) Another synonym of changeset. - Example: "I've pushed an update". + Example: "I've pushed an update." (Verb) This term is usually used to describe updating the state of the working directory to that of a specific changeset. See :hg:`help update`. - Example: "You should update". + Example: "You should update." Working directory See 'Directory, working'. diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/help/templates.txt --- a/mercurial/help/templates.txt Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/help/templates.txt Tue Sep 01 17:09:00 2015 -0500 @@ -69,6 +69,10 @@ $ hg log -r 0 --template "{date(date, '%Y')}\n" +- Display date in UTC:: + + $ hg log -r 0 --template "{localdate(date, 'UTC')|date}\n" + - Output the description set to a fill-width of 30:: $ hg log -r 0 --template "{fill(desc, 30)}" diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/hg.py --- a/mercurial/hg.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/hg.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,17 +6,41 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -from lock import release -from node import nullid +from __future__ import absolute_import + +import errno +import os +import shutil + +from .i18n import _ +from .node import nullid -import localrepo, bundlerepo, unionrepo, httppeer, sshpeer, statichttprepo -import bookmarks, lock, util, extensions, error, node, scmutil, phases, url -import cmdutil, discovery, repoview, exchange -import ui as uimod -import merge as mergemod -import verify as verifymod -import errno, os, shutil +from . import ( + bookmarks, + bundlerepo, + cmdutil, + discovery, + error, + exchange, + extensions, + httppeer, + localrepo, + lock, + merge as mergemod, + node, + phases, + repoview, + scmutil, + sshpeer, + statichttprepo, + ui as uimod, + unionrepo, + url, + util, + verify as verifymod, +) + +release = lock.release def _local(path): path = util.expandpath(util.urllocalpath(path)) @@ -558,7 +582,10 @@ try: uprev = destrepo.lookup(checkout) except error.RepoLookupError: - pass + try: + uprev = destrepo.lookup(update) + except error.RepoLookupError: + pass if uprev is None: try: uprev = destrepo._bookmarks['@'] diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/hgweb/hgweb_mod.py Tue Sep 01 17:09:00 2015 -0500 @@ -61,7 +61,79 @@ return reversed(breadcrumb) +class requestcontext(object): + """Holds state/context for an individual request. + + Servers can be multi-threaded. Holding state on the WSGI application + is prone to race conditions. Instances of this class exist to hold + mutable and race-free state for requests. + """ + def __init__(self, app): + object.__setattr__(self, 'app', app) + object.__setattr__(self, 'repo', app.repo) + + object.__setattr__(self, 'archives', ('zip', 'gz', 'bz2')) + + object.__setattr__(self, 'maxchanges', + self.configint('web', 'maxchanges', 10)) + object.__setattr__(self, 'stripecount', + self.configint('web', 'stripes', 1)) + object.__setattr__(self, 'maxshortchanges', + self.configint('web', 'maxshortchanges', 60)) + object.__setattr__(self, 'maxfiles', + self.configint('web', 'maxfiles', 10)) + object.__setattr__(self, 'allowpull', + self.configbool('web', 'allowpull', True)) + + # Proxy unknown reads and writes to the application instance + # until everything is moved to us. + def __getattr__(self, name): + return getattr(self.app, name) + + def __setattr__(self, name, value): + return setattr(self.app, name, value) + + # Servers are often run by a user different from the repo owner. + # Trust the settings from the .hg/hgrc files by default. + def config(self, section, name, default=None, untrusted=True): + return self.repo.ui.config(section, name, default, + untrusted=untrusted) + + def configbool(self, section, name, default=False, untrusted=True): + return self.repo.ui.configbool(section, name, default, + untrusted=untrusted) + + def configint(self, section, name, default=None, untrusted=True): + return self.repo.ui.configint(section, name, default, + untrusted=untrusted) + + def configlist(self, section, name, default=None, untrusted=True): + return self.repo.ui.configlist(section, name, default, + untrusted=untrusted) + + archivespecs = { + 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None), + 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None), + 'zip': ('application/zip', 'zip', '.zip', None), + } + + def archivelist(self, nodeid): + allowed = self.configlist('web', 'allow_archive') + for typ, spec in self.archivespecs.iteritems(): + if typ in allowed or self.configbool('web', 'allow%s' % typ): + yield {'type': typ, 'extension': spec[2], 'node': nodeid} + class hgweb(object): + """HTTP server for individual repositories. + + Instances of this class serve HTTP responses for a particular + repository. + + Instances are typically used as WSGI applications. + + Some servers are multi-threaded. On these servers, there may + be multiple active threads inside __call__. + """ def __init__(self, repo, name=None, baseui=None): if isinstance(repo, str): if baseui: @@ -87,8 +159,6 @@ self.repostate = ((-1, -1), (-1, -1)) self.mtime = -1 self.reponame = name - self.archives = 'zip', 'gz', 'bz2' - self.stripecount = 1 # we use untrusted=False to prevent a repo owner from using # web.templates in .hg/hgrc to get access to any file readable # by the user running the CGI script @@ -105,10 +175,6 @@ return self.repo.ui.configbool(section, name, default, untrusted=untrusted) - def configlist(self, section, name, default=None, untrusted=True): - return self.repo.ui.configlist(section, name, default, - untrusted=untrusted) - def _getview(self, repo): """The 'web.view' config controls changeset filter to hgweb. Possible values are ``served``, ``visible`` and ``all``. Default is ``served``. @@ -129,7 +195,7 @@ else: return repo.filtered('served') - def refresh(self, request=None): + def refresh(self, request): repostate = [] # file of interrests mtime and size for meth, fname in foi: @@ -142,22 +208,21 @@ if repostate != self.repostate: r = hg.repository(self.repo.baseui, self.repo.url()) self.repo = self._getview(r) - self.maxchanges = int(self.config("web", "maxchanges", 10)) - self.stripecount = int(self.config("web", "stripes", 1)) - self.maxshortchanges = int(self.config("web", "maxshortchanges", - 60)) - self.maxfiles = int(self.config("web", "maxfiles", 10)) - self.allowpull = self.configbool("web", "allowpull", True) encoding.encoding = self.config("web", "encoding", encoding.encoding) # update these last to avoid threads seeing empty settings self.repostate = repostate # mtime is needed for ETag self.mtime = st.st_mtime - if request: - self.repo.ui.environ = request.env + + self.repo.ui.environ = request.env def run(self): + """Start a server from CGI environment. + + Modern servers should be using WSGI and should avoid this + method, if possible. + """ if not os.environ.get('GATEWAY_INTERFACE', '').startswith("CGI/1."): raise RuntimeError("This function is only intended to be " "called while running as a CGI script.") @@ -165,12 +230,21 @@ wsgicgi.launch(self) def __call__(self, env, respond): + """Run the WSGI application. + + This may be called by multiple threads. + """ req = wsgirequest(env, respond) return self.run_wsgi(req) def run_wsgi(self, req): + """Internal method to run the WSGI application. + This is typically only called by Mercurial. External consumers + should be using instances of this class as the WSGI application. + """ self.refresh(req) + rctx = requestcontext(self) # work with CGI variables to create coherent structure # use SCRIPT_NAME, PATH_INFO and QUERY_STRING as well as our REPO_NAME @@ -201,7 +275,7 @@ if query: raise ErrorResponse(HTTP_NOT_FOUND) if cmd in perms: - self.check_perm(req, perms[cmd]) + self.check_perm(rctx, req, perms[cmd]) return protocol.call(self.repo, req, cmd) except ErrorResponse as inst: # A client that sends unbundle without 100-continue will @@ -247,7 +321,7 @@ if cmd == 'archive': fn = req.form['node'][0] - for type_, spec in self.archive_specs.iteritems(): + for type_, spec in rctx.archivespecs.iteritems(): ext = spec[2] if fn.endswith(ext): req.form['node'] = [fn[:-len(ext)]] @@ -262,7 +336,7 @@ # check read permissions non-static content if cmd != 'static': - self.check_perm(req, None) + self.check_perm(rctx, req, None) if cmd == '': req.form['cmd'] = [tmpl.cache['default']] @@ -275,9 +349,9 @@ raise ErrorResponse(HTTP_BAD_REQUEST, msg) elif cmd == 'file' and 'raw' in req.form.get('style', []): self.ctype = ctype - content = webcommands.rawfile(self, req, tmpl) + content = webcommands.rawfile(rctx, req, tmpl) else: - content = getattr(webcommands, cmd)(self, req, tmpl) + content = getattr(webcommands, cmd)(rctx, req, tmpl) req.respond(HTTP_OK, ctype) return content @@ -408,18 +482,6 @@ }) return tmpl - def archivelist(self, nodeid): - allowed = self.configlist("web", "allow_archive") - for i, spec in self.archive_specs.iteritems(): - if i in allowed or self.configbool("web", "allow" + i): - yield {"type" : i, "extension" : spec[2], "node" : nodeid} - - archive_specs = { - 'bz2': ('application/x-bzip2', 'tbz2', '.tar.bz2', None), - 'gz': ('application/x-gzip', 'tgz', '.tar.gz', None), - 'zip': ('application/zip', 'zip', '.zip', None), - } - - def check_perm(self, req, op): + def check_perm(self, rctx, req, op): for permhook in permhooks: - permhook(self, req, op) + permhook(rctx, req, op) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/hgweb/hgwebdir_mod.py --- a/mercurial/hgweb/hgwebdir_mod.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/hgweb/hgwebdir_mod.py Tue Sep 01 17:09:00 2015 -0500 @@ -79,17 +79,30 @@ return name, str(port), path class hgwebdir(object): - refreshinterval = 20 + """HTTP server for multiple repositories. + Given a configuration, different repositories will be served depending + on the request path. + + Instances are typically used as WSGI applications. + """ def __init__(self, conf, baseui=None): self.conf = conf self.baseui = baseui + self.ui = None self.lastrefresh = 0 self.motd = None self.refresh() def refresh(self): - if self.lastrefresh + self.refreshinterval > time.time(): + refreshinterval = 20 + if self.ui: + refreshinterval = self.ui.configint('web', 'refreshinterval', + refreshinterval) + + # refreshinterval <= 0 means to always refresh. + if (refreshinterval > 0 and + self.lastrefresh + refreshinterval > time.time()): return if self.baseui: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/hgweb/request.py --- a/mercurial/hgweb/request.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/hgweb/request.py Tue Sep 01 17:09:00 2015 -0500 @@ -40,6 +40,12 @@ return form class wsgirequest(object): + """Higher-level API for a WSGI request. + + WSGI applications are invoked with 2 arguments. They are used to + instantiate instances of this class, which provides higher-level APIs + for obtaining request parameters, writing HTTP output, etc. + """ def __init__(self, wsgienv, start_response): version = wsgienv['wsgi.version'] if (version < (1, 0)) or (version >= (2, 0)): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/hgweb/webcommands.py --- a/mercurial/hgweb/webcommands.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/hgweb/webcommands.py Tue Sep 01 17:09:00 2015 -0500 @@ -639,35 +639,10 @@ The ``branches`` template is rendered. """ - tips = [] - heads = web.repo.heads() - parity = paritygen(web.stripecount) - sortkey = lambda item: (not item[1], item[0].rev()) - - def entries(limit, **map): - count = 0 - if not tips: - for tag, hs, tip, closed in web.repo.branchmap().iterbranches(): - tips.append((web.repo[tip], closed)) - for ctx, closed in sorted(tips, key=sortkey, reverse=True): - if limit > 0 and count >= limit: - return - count += 1 - if closed: - status = 'closed' - elif ctx.node() not in heads: - status = 'inactive' - else: - status = 'open' - yield {'parity': parity.next(), - 'branch': ctx.branch(), - 'status': status, - 'node': ctx.hex(), - 'date': ctx.date()} - + entries = webutil.branchentries(web.repo, web.stripecount) + latestentry = webutil.branchentries(web.repo, web.stripecount, 1) return tmpl('branches', node=hex(web.repo.changelog.tip()), - entries=lambda **x: entries(0, **x), - latestentry=lambda **x: entries(1, **x)) + entries=entries, latestentry=latestentry) @webcommand('summary') def summary(web, req, tmpl): @@ -710,18 +685,6 @@ 'date': web.repo[n].date(), 'node': hex(n)} - def branches(**map): - parity = paritygen(web.stripecount) - - b = web.repo.branchmap() - l = [(-web.repo.changelog.rev(tip), tip, tag) - for tag, heads, tip, closed in b.iterbranches()] - for r, n, t in sorted(l): - yield {'parity': parity.next(), - 'branch': t, - 'node': hex(n), - 'date': web.repo[n].date()} - def changelist(**map): parity = paritygen(web.stripecount, offset=start - end) l = [] # build a list in forward order for efficiency @@ -761,7 +724,7 @@ lastchange=tip.date(), tags=tagentries, bookmarks=bookmarks, - branches=branches, + branches=webutil.branchentries(web.repo, web.stripecount, 10), shortlog=changelist, node=tip.hex(), symrev='tip', @@ -1115,7 +1078,7 @@ raise ErrorResponse(HTTP_NOT_FOUND, 'file(s) not found: %s' % file[0]) - mimetype, artype, extension, encoding = web.archive_specs[type_] + mimetype, artype, extension, encoding = web.archivespecs[type_] headers = [ ('Content-Disposition', 'attachment; filename=%s%s' % (name, extension)) ] diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/hgweb/webutil.py --- a/mercurial/hgweb/webutil.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/hgweb/webutil.py Tue Sep 01 17:09:00 2015 -0500 @@ -199,11 +199,42 @@ for t in repo.nodebookmarks(node): yield tmpl(t1, bookmark=t, **args) +def branchentries(repo, stripecount, limit=0): + tips = [] + heads = repo.heads() + parity = paritygen(stripecount) + sortkey = lambda item: (not item[1], item[0].rev()) + + def entries(**map): + count = 0 + if not tips: + for tag, hs, tip, closed in repo.branchmap().iterbranches(): + tips.append((repo[tip], closed)) + for ctx, closed in sorted(tips, key=sortkey, reverse=True): + if limit > 0 and count >= limit: + return + count += 1 + if closed: + status = 'closed' + elif ctx.node() not in heads: + status = 'inactive' + else: + status = 'open' + yield { + 'parity': parity.next(), + 'branch': ctx.branch(), + 'status': status, + 'node': ctx.hex(), + 'date': ctx.date() + } + + return entries + def cleanpath(repo, path): path = path.lstrip('/') return pathutil.canonpath(repo.root, '', path) -def changeidctx (repo, changeid): +def changeidctx(repo, changeid): try: ctx = repo[changeid] except error.RepoError: @@ -212,11 +243,11 @@ return ctx -def changectx (repo, req): +def changectx(repo, req): changeid = "tip" if 'node' in req.form: changeid = req.form['node'][0] - ipos=changeid.find(':') + ipos = changeid.find(':') if ipos != -1: changeid = changeid[(ipos + 1):] elif 'manifest' in req.form: @@ -227,7 +258,7 @@ def basechangectx(repo, req): if 'node' in req.form: changeid = req.form['node'][0] - ipos=changeid.find(':') + ipos = changeid.find(':') if ipos != -1: changeid = changeid[:ipos] return changeidctx(repo, changeid) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/hook.py --- a/mercurial/hook.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/hook.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,19 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import os, sys, time -import extensions, util, demandimport, error +from __future__ import absolute_import + +import os +import sys +import time + +from .i18n import _ +from . import ( + demandimport, + error, + extensions, + util, +) def _pythonhook(ui, repo, name, hname, funcname, args, throw): '''call python hook. hook is callable object, looked up as diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/httppeer.py --- a/mercurial/httppeer.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/httppeer.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,12 +6,28 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullid -from i18n import _ +from __future__ import absolute_import + +import errno +import httplib +import os +import socket import tempfile -import changegroup, statichttprepo, error, httpconnection, url, util, wireproto -import os, urllib, urllib2, zlib, httplib -import errno, socket +import urllib +import urllib2 +import zlib + +from .i18n import _ +from .node import nullid +from . import ( + changegroup, + error, + httpconnection, + statichttprepo, + url, + util, + wireproto, +) def zgenerator(f): zd = zlib.decompressobj() diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/i18n.py --- a/mercurial/i18n.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/i18n.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,8 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import encoding -import gettext as gettextmod, sys, os, locale +from __future__ import absolute_import + +import gettext as gettextmod +import locale +import os +import sys + +from . import encoding # modelled after templater.templatepath: if getattr(sys, 'frozen', None) is not None: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/localrepo.py --- a/mercurial/localrepo.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/localrepo.py Tue Sep 01 17:09:00 2015 -0500 @@ -354,6 +354,10 @@ manifestcachesize = self.ui.configint('format', 'manifestcachesize') if manifestcachesize is not None: self.svfs.options['manifestcachesize'] = manifestcachesize + # experimental config: format.aggressivemergedeltas + aggressivemergedeltas = self.ui.configbool('format', + 'aggressivemergedeltas', False) + self.svfs.options['aggressivemergedeltas'] = aggressivemergedeltas def _writerequirements(self): scmutil.writerequires(self.vfs, self.requirements) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/lock.py --- a/mercurial/lock.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/lock.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,10 +5,19 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import util, error -import errno, os, socket, time +from __future__ import absolute_import + +import errno +import os +import socket +import time import warnings +from . import ( + error, + util, +) + class lock(object): '''An advisory lock held by one process to control access to a set of files. Non-cooperating processes or incorrectly written scripts @@ -56,7 +65,7 @@ timeout = self.timeout while True: try: - self.trylock() + self._trylock() return self.timeout - timeout except error.LockHeld as inst: if timeout != 0: @@ -67,14 +76,16 @@ raise error.LockHeld(errno.ETIMEDOUT, inst.filename, self.desc, inst.locker) - def trylock(self): + def _trylock(self): if self.held: self.held += 1 return if lock._host is None: lock._host = socket.gethostname() lockname = '%s:%s' % (lock._host, self.pid) - while not self.held: + retry = 5 + while not self.held and retry: + retry -= 1 try: self.vfs.makelock(lockname, self.f) self.held = 1 diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/mail.py --- a/mercurial/mail.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/mail.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,10 +5,22 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import util, encoding, sslutil -import os, smtplib, socket, quopri, time, sys +from __future__ import absolute_import + import email +import os +import quopri +import smtplib +import socket +import sys +import time + +from .i18n import _ +from . import ( + encoding, + sslutil, + util, +) _oldheaderinit = email.Header.Header.__init__ def _unifiedheaderinit(self, *args, **kw): @@ -19,10 +31,10 @@ constructor, and 2.7 removed this parameter. Default argument is continuation_ws=' ', which means that the - behaviour is different in <2.7 and 2.7 + behavior is different in <2.7 and 2.7 - We consider the 2.7 behaviour to be preferable, but need - to have an unified behaviour for versions 2.4 to 2.7 + We consider the 2.7 behavior to be preferable, but need + to have an unified behavior for versions 2.4 to 2.7 """ # override continuation_ws kw['continuation_ws'] = ' ' diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/match.py --- a/mercurial/match.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/match.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,17 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import copy, os, re -import util, pathutil -from i18n import _ +from __future__ import absolute_import + +import copy +import os +import re + +from .i18n import _ +from . import ( + pathutil, + util, +) propertycache = util.propertycache diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/merge.py --- a/mercurial/merge.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/merge.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,13 +5,29 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + +import errno +import os +import shutil import struct -from node import nullid, nullrev, hex, bin -from i18n import _ -from mercurial import obsolete -import error as errormod, util, filemerge, copies, subrepo, worker -import errno, os, shutil +from .i18n import _ +from .node import ( + bin, + hex, + nullid, + nullrev, +) +from . import ( + copies, + error as errormod, + filemerge, + obsolete, + subrepo, + util, + worker, +) _pack = struct.pack _unpack = struct.unpack diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/minirst.py --- a/mercurial/minirst.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/minirst.py Tue Sep 01 17:09:00 2015 -0500 @@ -18,11 +18,16 @@ when adding support for new constructs. """ -import re -import util, encoding -from i18n import _ +from __future__ import absolute_import import cgi +import re + +from .i18n import _ +from . import ( + encoding, + util, +) def section(s): return "%s\n%s\n\n" % (s, "\"" * encoding.colwidth(s)) @@ -651,13 +656,17 @@ def format(text, width=80, indent=0, keep=None, style='plain', section=None): """Parse and format the text according to width.""" blocks, pruned = parse(text, indent, keep or []) + parents = [] if section: sections = getsections(blocks) blocks = [] i = 0 while i < len(sections): name, nest, b = sections[i] + del parents[nest:] + parents.append(name) if name == section: + b[0]['path'] = parents[3:] blocks.extend(b) ## Also show all subnested sections @@ -669,6 +678,14 @@ if style == 'html': text = formathtml(blocks) else: + if len([b for b in blocks if b['type'] == 'definition']) > 1: + i = 0 + while i < len(blocks): + if blocks[i]['type'] == 'definition': + if 'path' in blocks[i]: + blocks[i]['lines'][0] = '"%s"' % '.'.join( + blocks[i]['path']) + i += 1 text = ''.join(formatblock(b, width) for b in blocks) if keep is None: return text diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/namespaces.py --- a/mercurial/namespaces.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/namespaces.py Tue Sep 01 17:09:00 2015 -0500 @@ -1,6 +1,10 @@ -from i18n import _ -from mercurial import util -import templatekw +from __future__ import absolute_import + +from .i18n import _ +from . import ( + templatekw, + util, +) def tolist(val): """ diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/node.py --- a/mercurial/node.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/node.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,6 +5,8 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import binascii nullrev = -1 diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/parser.py --- a/mercurial/parser.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/parser.py Tue Sep 01 17:09:00 2015 -0500 @@ -16,8 +16,10 @@ # an action is a tree node name, a tree label, and an optional match # __call__(program) parses program into a labeled tree -import error -from i18n import _ +from __future__ import absolute_import + +from .i18n import _ +from . import error class parser(object): def __init__(self, elements, methods=None): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/parsers.c --- a/mercurial/parsers.c Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/parsers.c Tue Sep 01 17:09:00 2015 -0500 @@ -253,8 +253,11 @@ if (normed == NULL) goto quit; - if (PyDict_SetItem(file_foldmap, normed, k) == -1) + if (PyDict_SetItem(file_foldmap, normed, k) == -1) { + Py_DECREF(normed); goto quit; + } + Py_DECREF(normed); } } return file_foldmap; @@ -475,14 +478,14 @@ &str, &readlen)) goto quit; - if (readlen < 0) - goto quit; - len = readlen; /* read parents */ - if (len < 40) + if (len < 40) { + PyErr_SetString( + PyExc_ValueError, "too little data for parents"); goto quit; + } parents = Py_BuildValue("s#s#", str, 20, str + 20, 20); if (!parents) @@ -679,7 +682,7 @@ } nodetree; /* - * This class has two behaviours. + * This class has two behaviors. * * When used in a list-like way (with integer keys), we decode an * entry in a RevlogNG index file on demand. Our last entry is a @@ -702,8 +705,8 @@ PyObject *headrevs; /* cache, invalidated on changes */ PyObject *filteredrevs;/* filtered revs set */ nodetree *nt; /* base-16 trie */ - int ntlength; /* # nodes in use */ - int ntcapacity; /* # nodes allocated */ + unsigned ntlength; /* # nodes in use */ + unsigned ntcapacity; /* # nodes allocated */ int ntdepth; /* maximum depth of tree */ int ntsplits; /* # splits performed */ int ntrev; /* last rev scanned */ @@ -1043,13 +1046,12 @@ return newlist; } -/* arg should be Py_ssize_t but Python 2.4 do not support the n format */ -static int check_filter(PyObject *filter, unsigned long arg) { +static int check_filter(PyObject *filter, Py_ssize_t arg) { if (filter) { PyObject *arglist, *result; int isfiltered; - arglist = Py_BuildValue("(k)", arg); + arglist = Py_BuildValue("(n)", arg); if (!arglist) { return -1; } @@ -1105,6 +1107,162 @@ phases[i] = phases[parent_2]; } +static PyObject *reachableroots2(indexObject *self, PyObject *args) +{ + + /* Input */ + long minroot; + PyObject *includepatharg = NULL; + int includepath = 0; + /* heads and roots are lists */ + PyObject *heads = NULL; + PyObject *roots = NULL; + PyObject *reachable = NULL; + + PyObject *val; + Py_ssize_t len = index_length(self) - 1; + long revnum; + Py_ssize_t k; + Py_ssize_t i; + Py_ssize_t l; + int r; + int parents[2]; + + /* Internal data structure: + * tovisit: array of length len+1 (all revs + nullrev), filled upto lentovisit + * revstates: array of length len+1 (all revs + nullrev) */ + int *tovisit = NULL; + long lentovisit = 0; + enum { RS_SEEN = 1, RS_ROOT = 2, RS_REACHABLE = 4 }; + char *revstates = NULL; + + /* Get arguments */ + if (!PyArg_ParseTuple(args, "lO!O!O!", &minroot, &PyList_Type, &heads, + &PyList_Type, &roots, + &PyBool_Type, &includepatharg)) + goto bail; + + if (includepatharg == Py_True) + includepath = 1; + + /* Initialize return set */ + reachable = PyList_New(0); + if (reachable == NULL) + goto bail; + + /* Initialize internal datastructures */ + tovisit = (int *)malloc((len + 1) * sizeof(int)); + if (tovisit == NULL) { + PyErr_NoMemory(); + goto bail; + } + + revstates = (char *)calloc(len + 1, 1); + if (revstates == NULL) { + PyErr_NoMemory(); + goto bail; + } + + l = PyList_GET_SIZE(roots); + for (i = 0; i < l; i++) { + revnum = PyInt_AsLong(PyList_GET_ITEM(roots, i)); + if (revnum == -1 && PyErr_Occurred()) + goto bail; + /* If root is out of range, e.g. wdir(), it must be unreachable + * from heads. So we can just ignore it. */ + if (revnum + 1 < 0 || revnum + 1 >= len + 1) + continue; + revstates[revnum + 1] |= RS_ROOT; + } + + /* Populate tovisit with all the heads */ + l = PyList_GET_SIZE(heads); + for (i = 0; i < l; i++) { + revnum = PyInt_AsLong(PyList_GET_ITEM(heads, i)); + if (revnum == -1 && PyErr_Occurred()) + goto bail; + if (revnum + 1 < 0 || revnum + 1 >= len + 1) { + PyErr_SetString(PyExc_IndexError, "head out of range"); + goto bail; + } + if (!(revstates[revnum + 1] & RS_SEEN)) { + tovisit[lentovisit++] = (int)revnum; + revstates[revnum + 1] |= RS_SEEN; + } + } + + /* Visit the tovisit list and find the reachable roots */ + k = 0; + while (k < lentovisit) { + /* Add the node to reachable if it is a root*/ + revnum = tovisit[k++]; + if (revstates[revnum + 1] & RS_ROOT) { + revstates[revnum + 1] |= RS_REACHABLE; + val = PyInt_FromLong(revnum); + if (val == NULL) + goto bail; + r = PyList_Append(reachable, val); + Py_DECREF(val); + if (r < 0) + goto bail; + if (includepath == 0) + continue; + } + + /* Add its parents to the list of nodes to visit */ + if (revnum == -1) + continue; + r = index_get_parents(self, revnum, parents, (int)len - 1); + if (r < 0) + goto bail; + for (i = 0; i < 2; i++) { + if (!(revstates[parents[i] + 1] & RS_SEEN) + && parents[i] >= minroot) { + tovisit[lentovisit++] = parents[i]; + revstates[parents[i] + 1] |= RS_SEEN; + } + } + } + + /* Find all the nodes in between the roots we found and the heads + * and add them to the reachable set */ + if (includepath == 1) { + long minidx = minroot; + if (minidx < 0) + minidx = 0; + for (i = minidx; i < len; i++) { + if (!(revstates[i + 1] & RS_SEEN)) + continue; + r = index_get_parents(self, i, parents, (int)len - 1); + /* Corrupted index file, error is set from + * index_get_parents */ + if (r < 0) + goto bail; + if (((revstates[parents[0] + 1] | + revstates[parents[1] + 1]) & RS_REACHABLE) + && !(revstates[i + 1] & RS_REACHABLE)) { + revstates[i + 1] |= RS_REACHABLE; + val = PyInt_FromLong(i); + if (val == NULL) + goto bail; + r = PyList_Append(reachable, val); + Py_DECREF(val); + if (r < 0) + goto bail; + } + } + } + + free(revstates); + free(tovisit); + return reachable; +bail: + Py_XDECREF(reachable); + free(revstates); + free(tovisit); + return NULL; +} + static PyObject *compute_phases_map_sets(indexObject *self, PyObject *args) { PyObject *roots = Py_None; @@ -2011,16 +2169,18 @@ */ static PyObject *index_ancestors(indexObject *self, PyObject *args) { + PyObject *ret; PyObject *gca = index_commonancestorsheads(self, args); if (gca == NULL) return NULL; if (PyList_GET_SIZE(gca) <= 1) { - Py_INCREF(gca); return gca; } - return find_deepest(self, gca); + ret = find_deepest(self, gca); + Py_DECREF(gca); + return ret; } /* @@ -2282,6 +2442,8 @@ "get an index entry"}, {"computephasesmapsets", (PyCFunction)compute_phases_map_sets, METH_VARARGS, "compute phases"}, + {"reachableroots2", (PyCFunction)reachableroots2, METH_VARARGS, + "reachableroots"}, {"headrevs", (PyCFunction)index_headrevs, METH_VARARGS, "get head revisions"}, /* Can do filtering since 3.2 */ {"headrevsfiltered", (PyCFunction)index_headrevs, METH_VARARGS, @@ -2502,12 +2664,10 @@ static PyObject *fm1readmarkers(PyObject *self, PyObject *args) { const char *data; Py_ssize_t datalen; - /* only unsigned long because python 2.4, should be Py_ssize_t */ - unsigned long offset, stop; + Py_ssize_t offset, stop; PyObject *markers = NULL; - /* replace kk with nn when we drop Python 2.4 */ - if (!PyArg_ParseTuple(args, "s#kk", &data, &datalen, &offset, &stop)) { + if (!PyArg_ParseTuple(args, "s#nn", &data, &datalen, &offset, &stop)) { return NULL; } data += offset; diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/pathencode.c --- a/mercurial/pathencode.c Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/pathencode.c Tue Sep 01 17:09:00 2015 -0500 @@ -684,6 +684,8 @@ hashobj = PyObject_CallMethod(shaobj, "digest", ""); Py_DECREF(shaobj); + if (hashobj == NULL) + return -1; if (!PyString_Check(hashobj) || PyString_GET_SIZE(hashobj) != 20) { PyErr_SetString(PyExc_TypeError, diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/pathutil.py --- a/mercurial/pathutil.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/pathutil.py Tue Sep 01 17:09:00 2015 -0500 @@ -1,8 +1,15 @@ -import os, errno, stat, posixpath +from __future__ import absolute_import -import encoding -import util -from i18n import _ +import errno +import os +import posixpath +import stat + +from .i18n import _ +from . import ( + encoding, + util, +) def _lowerclean(s): return encoding.hfsignoreclean(s.lower()) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/peer.py --- a/mercurial/peer.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/peer.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,11 +6,91 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import error +from __future__ import absolute_import + +from .i18n import _ +from . import ( + error, + util, +) + +# abstract batching support + +class future(object): + '''placeholder for a value to be set later''' + def set(self, value): + if util.safehasattr(self, 'value'): + raise error.RepoError("future is already set") + self.value = value + +class batcher(object): + '''base class for batches of commands submittable in a single request + + All methods invoked on instances of this class are simply queued and + return a a future for the result. Once you call submit(), all the queued + calls are performed and the results set in their respective futures. + ''' + def __init__(self): + self.calls = [] + def __getattr__(self, name): + def call(*args, **opts): + resref = future() + self.calls.append((name, args, opts, resref,)) + return resref + return call + def submit(self): + pass + +class localbatch(batcher): + '''performs the queued calls directly''' + def __init__(self, local): + batcher.__init__(self) + self.local = local + def submit(self): + for name, args, opts, resref in self.calls: + resref.set(getattr(self.local, name)(*args, **opts)) + +def batchable(f): + '''annotation for batchable methods + + Such methods must implement a coroutine as follows: + + @batchable + def sample(self, one, two=None): + # Handle locally computable results first: + if not one: + yield "a local result", None + # Build list of encoded arguments suitable for your wire protocol: + encargs = [('one', encode(one),), ('two', encode(two),)] + # Create future for injection of encoded result: + encresref = future() + # Return encoded arguments and future: + yield encargs, encresref + # Assuming the future to be filled with the result from the batched + # request now. Decode it: + yield decode(encresref.value) + + The decorator returns a function which wraps this coroutine as a plain + method, but adds the original method as an attribute called "batchable", + which is used by remotebatch to split the call into separate encoding and + decoding phases. + ''' + def plain(*args, **opts): + batchable = f(*args, **opts) + encargsorres, encresref = batchable.next() + if not encresref: + return encargsorres # a local result in this case + self = args[0] + encresref.set(self._submitone(f.func_name, encargsorres)) + return batchable.next() + setattr(plain, 'batchable', f) + return plain class peerrepository(object): + def batch(self): + return localbatch(self) + def capable(self, name): '''tell whether repo supports named capability. return False if not supported. diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/phases.py --- a/mercurial/phases.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/phases.py Tue Sep 01 17:09:00 2015 -0500 @@ -100,11 +100,23 @@ """ -import os +from __future__ import absolute_import + import errno -from node import nullid, nullrev, bin, hex, short -from i18n import _ -import util, error +import os + +from .i18n import _ +from .node import ( + bin, + hex, + nullid, + nullrev, + short, +) +from . import ( + error, + util, +) allphases = public, draft, secret = range(3) trackedphases = allphases[1:] diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/posix.py --- a/mercurial/posix.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/posix.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,11 +5,26 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import encoding -import os, sys, errno, stat, getpass, pwd, grp, socket, tempfile, unicodedata +from __future__ import absolute_import + +import errno +import fcntl +import getpass +import grp +import os +import pwd +import re import select -import fcntl, re +import socket +import stat +import sys +import tempfile +import unicodedata + +from .i18n import _ +from . import ( + encoding, +) posixfile = open normpath = os.path.normpath @@ -459,7 +474,8 @@ def termwidth(): try: - import termios, array + import array + import termios for dev in (sys.stderr, sys.stdout, sys.stdin): try: try: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/progress.py --- a/mercurial/progress.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/progress.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,13 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import sys -import time import threading -from mercurial import encoding +import time -from mercurial.i18n import _ - +from .i18n import _ +from . import encoding def spacejoin(*args): return ' '.join(s for s in args if s) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/pushkey.py --- a/mercurial/pushkey.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/pushkey.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,7 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import bookmarks, phases, obsolete, encoding +from __future__ import absolute_import + +from . import ( + bookmarks, + encoding, + obsolete, + phases, +) def _nslist(repo): n = {} diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/repair.py --- a/mercurial/repair.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/repair.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,11 +6,19 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial import changegroup, exchange, util, bundle2 -from mercurial.node import short -from mercurial.i18n import _ +from __future__ import absolute_import + import errno +from .i18n import _ +from .node import short +from . import ( + bundle2, + changegroup, + exchange, + util, +) + def _bundle(repo, bases, heads, node, suffix, compress=True): """create a bundle with the specified revisions as a backup""" usebundle2 = (repo.ui.configbool('experimental', 'bundle2-exp', True) and diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/repoview.py --- a/mercurial/repoview.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/repoview.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,15 +6,20 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import heapq +from __future__ import absolute_import + import copy -import error -import phases -import util -import obsolete +import heapq import struct -import tags as tagsmod -from node import nullrev + +from .node import nullrev +from . import ( + error, + obsolete, + phases, + tags as tagsmod, + util, +) def hideablerevs(repo): """Revisions candidates to be hidden diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/revlog.py --- a/mercurial/revlog.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/revlog.py Tue Sep 01 17:09:00 2015 -0500 @@ -100,11 +100,10 @@ # 4 bytes: compressed length # 4 bytes: base rev # 4 bytes: link rev -# 32 bytes: parent 1 nodeid -# 32 bytes: parent 2 nodeid -# 32 bytes: nodeid +# 20 bytes: parent 1 nodeid +# 20 bytes: parent 2 nodeid +# 20 bytes: nodeid indexformatv0 = ">4l20s20s20s" -v0shaoffset = 56 class revlogoldio(object): def __init__(self): @@ -150,7 +149,6 @@ # 4 bytes: parent 2 rev # 32 bytes: nodeid indexformatng = ">Qiiiiii20s12x" -ngshaoffset = 32 versionformat = ">I" # corresponds to uncompressed length of indexformatng (2 gigs, 4-byte @@ -212,6 +210,7 @@ self._chunkcache = (0, '') self._chunkcachesize = 65536 self._maxchainlen = None + self._aggressivemergedeltas = False self.index = [] self._pcache = {} self._nodecache = {nullid: nullrev} @@ -229,6 +228,8 @@ self._chunkcachesize = opts['chunkcachesize'] if 'maxchainlen' in opts: self._maxchainlen = opts['maxchainlen'] + if 'aggressivemergedeltas' in opts: + self._aggressivemergedeltas = opts['aggressivemergedeltas'] if self._chunkcachesize <= 0: raise RevlogError(_('revlog chunk cache size %r is not greater ' @@ -1235,6 +1236,25 @@ return ('u', text) return ("", bin) + def _isgooddelta(self, d, textlen): + """Returns True if the given delta is good. Good means that it is within + the disk span, disk size, and chain length bounds that we know to be + performant.""" + if d is None: + return False + + # - 'dist' is the distance from the base revision -- bounding it limits + # the amount of I/O we need to do. + # - 'compresseddeltalen' is the sum of the total size of deltas we need + # to apply -- bounding it limits the amount of CPU we consume. + dist, l, data, base, chainbase, chainlen, compresseddeltalen = d + if (dist > textlen * 4 or l > textlen or + compresseddeltalen > textlen * 2 or + (self._maxchainlen and chainlen > self._maxchainlen)): + return False + + return True + def _addrevision(self, node, text, transaction, link, p1, p2, flags, cachedelta, ifh, dfh): """internal function to add revisions to the log @@ -1315,19 +1335,6 @@ basecache = self._basecache p1r, p2r = self.rev(p1), self.rev(p2) - # should we try to build a delta? - if prev != nullrev: - if self._generaldelta: - if p1r >= basecache[1]: - d = builddelta(p1r) - elif p2r >= basecache[1]: - d = builddelta(p2r) - else: - d = builddelta(prev) - else: - d = builddelta(prev) - dist, l, data, base, chainbase, chainlen, compresseddeltalen = d - # full versions are inserted when the needed deltas # become comparable to the uncompressed text if text is None: @@ -1336,13 +1343,42 @@ else: textlen = len(text) - # - 'dist' is the distance from the base revision -- bounding it limits - # the amount of I/O we need to do. - # - 'compresseddeltalen' is the sum of the total size of deltas we need - # to apply -- bounding it limits the amount of CPU we consume. - if (d is None or dist > textlen * 4 or l > textlen or - compresseddeltalen > textlen * 2 or - (self._maxchainlen and chainlen > self._maxchainlen)): + # should we try to build a delta? + if prev != nullrev: + if self._generaldelta: + if p2r != nullrev and self._aggressivemergedeltas: + d = builddelta(p1r) + d2 = builddelta(p2r) + p1good = self._isgooddelta(d, textlen) + p2good = self._isgooddelta(d2, textlen) + if p1good and p2good: + # If both are good deltas, choose the smallest + if d2[1] < d[1]: + d = d2 + elif p2good: + # If only p2 is good, use it + d = d2 + elif p1good: + pass + else: + # Neither is good, try against prev to hopefully save us + # a fulltext. + d = builddelta(prev) + else: + # Pick whichever parent is closer to us (to minimize the + # chance of having to build a fulltext). Since + # nullrev == -1, any non-merge commit will always pick p1r. + drev = p2r if p2r > p1r else p1r + d = builddelta(drev) + # If the chosen delta will result in us making a full text, + # give it one last try against prev. + if drev != prev and not self._isgooddelta(d, textlen): + d = builddelta(prev) + else: + d = builddelta(prev) + dist, l, data, base, chainbase, chainlen, compresseddeltalen = d + + if not self._isgooddelta(d, textlen): text = buildtext() data = self.compress(text) l = len(data[1]) + len(data[0]) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/revset.py --- a/mercurial/revset.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/revset.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,16 +5,25 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import re -import parser, util, error, hbisect, phases -import node +from __future__ import absolute_import + import heapq -import match as matchmod -from i18n import _ -import encoding -import obsolete as obsmod -import pathutil -import repoview +import re + +from .i18n import _ +from . import ( + encoding, + error, + hbisect, + match as matchmod, + node, + obsolete as obsmod, + parser, + pathutil, + phases, + repoview, + util, +) def _revancestors(repo, revs, followfirst): """Like revlog.ancestors(), but supports followfirst.""" @@ -78,19 +87,17 @@ return generatorset(iterate(), iterasc=True) -def _revsbetween(repo, roots, heads): - """Return all paths between roots and heads, inclusive of both endpoint - sets.""" +def _reachablerootspure(repo, minroot, roots, heads, includepath): + """return (heads(:: and ::)) + + If includepath is True, return (::).""" if not roots: - return baseset() + return [] parentrevs = repo.changelog.parentrevs + roots = set(roots) visit = list(heads) reachable = set() seen = {} - # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset - # (and if it is not, it should.) - minroot = min(roots) - roots = set(roots) # prefetch all the things! (because python is slow) reached = reachable.add dovisit = visit.append @@ -101,6 +108,8 @@ rev = nextvisit() if rev in roots: reached(rev) + if not includepath: + continue parents = parentrevs(rev) seen[rev] = parents for parent in parents: @@ -108,11 +117,30 @@ dovisit(parent) if not reachable: return baseset() + if not includepath: + return reachable for rev in sorted(seen): for parent in seen[rev]: if parent in reachable: reached(rev) - return baseset(sorted(reachable)) + return reachable + +def reachableroots(repo, roots, heads, includepath=False): + """return (heads(:: and ::)) + + If includepath is True, return (::).""" + if not roots: + return baseset() + minroot = roots.min() + roots = list(roots) + heads = list(heads) + try: + revs = repo.changelog.reachableroots(minroot, heads, roots, includepath) + except AttributeError: + revs = _reachablerootspure(repo, minroot, roots, heads, includepath) + revs = baseset(revs) + revs.sort() + return revs elements = { # token-type: binding-strength, primary, prefix, infix, suffix @@ -178,6 +206,21 @@ if symletters is None: symletters = _symletters + if program and lookup: + # attempt to parse old-style ranges first to deal with + # things like old-tag which contain query metacharacters + parts = program.split(':', 1) + if all(lookup(sym) for sym in parts if sym): + if parts[0]: + yield ('symbol', parts[0], 0) + if len(parts) > 1: + s = len(parts[0]) + yield (':', None, s) + if parts[1]: + yield ('symbol', parts[1], s + 1) + yield ('end', None, len(program)) + return + pos, l = 0, len(program) while pos < l: c = program[pos] @@ -382,7 +425,8 @@ def dagrange(repo, subset, x, y): r = fullreposet(repo) - xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y)) + xs = reachableroots(repo, getset(repo, r, x), getset(repo, r, y), + includepath=True) # XXX We should combine with subset first: 'subset & baseset(...)'. This is # necessary to ensure we preserve the order in subset. return xs & subset @@ -391,8 +435,13 @@ return getset(repo, getset(repo, subset, x), y) def orset(repo, subset, *xs): - rs = [getset(repo, subset, x) for x in xs] - return _combinesets(rs) + 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:]) + return a + b def notset(repo, subset, x): return subset - getset(repo, subset, x) @@ -990,34 +1039,37 @@ return limit(repo, subset, x) def _follow(repo, subset, x, name, followfirst=False): - l = getargs(x, 0, 1, _("%s takes no arguments or a filename") % name) + l = getargs(x, 0, 1, _("%s takes no arguments or a pattern") % name) c = repo['.'] if l: - x = getstring(l[0], _("%s expected a filename") % name) - if x in c: - cx = c[x] - s = set(ctx.rev() for ctx in cx.ancestors(followfirst=followfirst)) - # include the revision responsible for the most recent version - s.add(cx.introrev()) - else: - return baseset() + x = getstring(l[0], _("%s expected a pattern") % name) + matcher = matchmod.match(repo.root, repo.getcwd(), [x], + ctx=repo[None], default='path') + + s = set() + for fname in c: + if matcher(fname): + fctx = c[fname] + s = s.union(set(c.rev() for c in fctx.ancestors(followfirst))) + # include the revision responsible for the most recent version + s.add(fctx.introrev()) else: s = _revancestors(repo, baseset([c.rev()]), followfirst) return subset & s def follow(repo, subset, x): - """``follow([file])`` + """``follow([pattern])`` An alias for ``::.`` (ancestors of the working directory's first parent). - If a filename is specified, the history of the given file is followed, - including copies. + If pattern is specified, the histories of files matching given + pattern is followed, including copies. """ return _follow(repo, subset, x, 'follow') def _followfirst(repo, subset, x): - # ``followfirst([file])`` - # Like ``follow([file])`` but follows only the first parent of - # every revision or file revision. + # ``followfirst([pattern])`` + # Like ``follow([pattern])`` but follows only the first parent of + # every revisions or files revisions. return _follow(repo, subset, x, '_followfirst', followfirst=True) def getall(repo, subset, x): @@ -1415,8 +1467,10 @@ default push location. """ # Avoid cycles. - import discovery - import hg + from . import ( + discovery, + hg, + ) # i18n: "outgoing" is a keyword l = getargs(x, 0, 1, _("outgoing takes one or no arguments")) # i18n: "outgoing" is a keyword @@ -1597,7 +1651,7 @@ synonym for the current local branch. """ - import hg # avoid start-up nasties + from . import hg # avoid start-up nasties # i18n: "remote" is a keyword l = getargs(x, 0, 2, _("remote takes one, two or no arguments")) @@ -2654,6 +2708,27 @@ if repo: lookup = repo.__contains__ tree = parse(spec, lookup) + return _makematcher(ui, tree, repo) + +def matchany(ui, specs, repo=None): + """Create a matcher that will include any revisions matching one of the + given specs""" + if not specs: + def mfunc(repo, subset=None): + return baseset() + return mfunc + if not all(specs): + raise error.ParseError(_("empty query")) + lookup = None + if repo: + lookup = repo.__contains__ + 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): if ui: tree = findaliases(ui, tree, showwarning=ui.warn) tree = foldconcat(tree) @@ -2813,6 +2888,7 @@ """True if the set will iterate in descending order""" raise NotImplementedError() + @util.cachefunc def min(self): """return the minimum element in the set""" if self.fastasc is not None: @@ -2821,6 +2897,7 @@ raise ValueError('arg is an empty sequence') return min(self) + @util.cachefunc def max(self): """return the maximum element in the set""" if self.fastdesc is not None: @@ -2896,6 +2973,8 @@ """ def __init__(self, data=()): if not isinstance(data, list): + if isinstance(data, set): + self._set = data data = list(data) self._list = data self._ascending = None @@ -3073,20 +3152,6 @@ def __repr__(self): return '<%s %r>' % (type(self).__name__, self._subset) -# this function will be removed, or merged to addset or orset, when -# - scmutil.revrange() can be rewritten to not combine calculated smartsets -# - or addset can handle more than two sets without balanced tree -def _combinesets(subsets): - """Create balanced tree of addsets representing union of given sets""" - if not subsets: - return baseset() - if len(subsets) == 1: - return subsets[0] - p = len(subsets) // 2 - xs = _combinesets(subsets[:p]) - ys = _combinesets(subsets[p:]) - return addset(xs, ys) - def _iterordered(ascending, iter1, iter2): """produce an ordered iteration from two iterators with the same order diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/scmutil.py --- a/mercurial/scmutil.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/scmutil.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,7 +6,7 @@ # GNU General Public License version 2 or any later version. from i18n import _ -from mercurial.node import nullrev, wdirrev +from mercurial.node import wdirrev import util, error, osutil, revset, similar, encoding, phases import pathutil import match as matchmod @@ -690,6 +690,11 @@ raise util.Abort(_('empty revision set')) return repo[l.last()] +def _pairspec(revspec): + tree = revset.parse(revspec) + tree = revset.optimize(tree, True)[1] # fix up "x^:y" -> "(x^):y" + return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall') + def revpair(repo, revs): if not revs: return repo.dirstate.p1(), None @@ -711,67 +716,21 @@ if first is None: raise util.Abort(_('empty revision range')) - if first == second and len(revs) == 1 and _revrangesep not in revs[0]: + # if top-level is range expression, the result must always be a pair + if first == second and len(revs) == 1 and not _pairspec(revs[0]): return repo.lookup(first), None return repo.lookup(first), repo.lookup(second) -_revrangesep = ':' - def revrange(repo, revs): """Yield revision as strings from a list of revision specifications.""" - - def revfix(repo, val, defval): - if not val and val != 0 and defval is not None: - return defval - return repo[val].rev() - - subsets = [] - - revsetaliases = [alias for (alias, _) in - repo.ui.configitems("revsetalias")] - + allspecs = [] for spec in revs: - # attempt to parse old-style ranges first to deal with - # things like old-tag which contain query metacharacters - try: - # ... except for revset aliases without arguments. These - # should be parsed as soon as possible, because they might - # clash with a hash prefix. - if spec in revsetaliases: - raise error.RepoLookupError - - if isinstance(spec, int): - subsets.append(revset.baseset([spec])) - continue - - if _revrangesep in spec: - start, end = spec.split(_revrangesep, 1) - if start in revsetaliases or end in revsetaliases: - raise error.RepoLookupError - - start = revfix(repo, start, 0) - end = revfix(repo, end, len(repo) - 1) - if end == nullrev and start < 0: - start = nullrev - if start < end: - l = revset.spanset(repo, start, end + 1) - else: - l = revset.spanset(repo, start, end - 1) - subsets.append(l) - continue - elif spec and spec in repo: # single unquoted rev - rev = revfix(repo, spec, None) - subsets.append(revset.baseset([rev])) - continue - except error.RepoLookupError: - pass - - # fall through to new-style queries if old-style fails - m = revset.match(repo.ui, spec, repo) - subsets.append(m(repo)) - - return revset._combinesets(subsets) + if isinstance(spec, int): + spec = revset.formatspec('rev(%d)', spec) + allspecs.append(spec) + m = revset.matchany(repo.ui, allspecs, repo) + return m(repo) def expandpats(pats): '''Expand bare globs when running on windows. @@ -1103,7 +1062,7 @@ Mercurial either atomic renames or appends for files under .hg, so to ensure the cache is reliable we need the filesystem to be able to tell us if a file has been replaced. If it can't, we fallback to - recreating the object on every call (essentially the same behaviour as + recreating the object on every call (essentially the same behavior as propertycache). ''' diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/setdiscovery.py --- a/mercurial/setdiscovery.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/setdiscovery.py Tue Sep 01 17:09:00 2015 -0500 @@ -40,11 +40,20 @@ classified with it (since all ancestors or descendants will be marked as well). """ +from __future__ import absolute_import + import collections -from node import nullid, nullrev -from i18n import _ import random -import util, dagutil + +from .i18n import _ +from .node import ( + nullid, + nullrev, +) +from . import ( + dagutil, + util, +) def _updatesample(dag, nodes, sample, quicksamplesize=0): """update an existing sample to match the expected size @@ -138,22 +147,12 @@ sample = _limitsample(ownheads, initialsamplesize) # indices between sample and externalized version must match sample = list(sample) - if remote.local(): - # stopgap until we have a proper localpeer that supports batch() - srvheadhashes = remote.heads() - yesno = remote.known(dag.externalizeall(sample)) - elif remote.capable('batch'): - batch = remote.batch() - srvheadhashesref = batch.heads() - yesnoref = batch.known(dag.externalizeall(sample)) - batch.submit() - srvheadhashes = srvheadhashesref.value - yesno = yesnoref.value - else: - # compatibility with pre-batch, but post-known remotes during 1.9 - # development - srvheadhashes = remote.heads() - sample = [] + batch = remote.batch() + srvheadhashesref = batch.heads() + yesnoref = batch.known(dag.externalizeall(sample)) + batch.submit() + srvheadhashes = srvheadhashesref.value + yesno = yesnoref.value if cl.tip() == nullid: if srvheadhashes != [nullid]: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/simplemerge.py --- a/mercurial/simplemerge.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/simplemerge.py Tue Sep 01 17:09:00 2015 -0500 @@ -16,9 +16,17 @@ # mbp: "you know that thing where cvs gives you conflict markers?" # s: "i hate that." -from i18n import _ -import scmutil, util, mdiff -import sys, os +from __future__ import absolute_import + +import os +import sys + +from .i18n import _ +from . import ( + mdiff, + scmutil, + util, +) class CantReprocessAndShowBase(Exception): pass @@ -92,9 +100,9 @@ newline = '\r\n' elif self.a[0].endswith('\r'): newline = '\r' - if name_a: + if name_a and start_marker: start_marker = start_marker + ' ' + name_a - if name_b: + if name_b and end_marker: end_marker = end_marker + ' ' + name_b if name_base and base_marker: base_marker = base_marker + ' ' + name_base @@ -112,17 +120,20 @@ yield self.b[i] elif what == 'conflict': self.conflicts = True - yield start_marker + newline + if start_marker is not None: + yield start_marker + newline for i in range(t[3], t[4]): yield self.a[i] if base_marker is not None: yield base_marker + newline for i in range(t[1], t[2]): yield self.base[i] - yield mid_marker + newline + if mid_marker is not None: + yield mid_marker + newline for i in range(t[5], t[6]): yield self.b[i] - yield end_marker + newline + if end_marker is not None: + yield end_marker + newline else: raise ValueError(what) @@ -345,18 +356,24 @@ raise util.Abort(msg) return text - name_a = local - name_b = other - name_base = None - labels = opts.get('label', []) - if len(labels) > 0: - name_a = labels[0] - if len(labels) > 1: - name_b = labels[1] - if len(labels) > 2: - name_base = labels[2] - if len(labels) > 3: - raise util.Abort(_("can only specify three labels.")) + mode = opts.get('mode','merge') + if mode == 'union': + name_a = None + name_b = None + name_base = None + else: + name_a = local + name_b = other + name_base = None + labels = opts.get('label', []) + if len(labels) > 0: + name_a = labels[0] + if len(labels) > 1: + name_b = labels[1] + if len(labels) > 2: + name_base = labels[2] + if len(labels) > 3: + raise util.Abort(_("can only specify three labels.")) try: localtext = readfile(local) @@ -374,7 +391,11 @@ m3 = Merge3Text(basetext, localtext, othertext) extrakwargs = {} - if name_base is not None: + if mode == 'union': + extrakwargs['start_marker'] = None + extrakwargs['mid_marker'] = None + extrakwargs['end_marker'] = None + elif name_base is not None: extrakwargs['base_marker'] = '|||||||' extrakwargs['name_base'] = name_base for line in m3.merge_lines(name_a=name_a, name_b=name_b, **extrakwargs): @@ -383,7 +404,7 @@ if not opts.get('print'): out.close() - if m3.conflicts: + if m3.conflicts and not mode == 'union': if not opts.get('quiet'): ui.warn(_("warning: conflicts during merge.\n")) return 1 diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/sshpeer.py --- a/mercurial/sshpeer.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/sshpeer.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import re -from i18n import _ -import util, error, wireproto + +from .i18n import _ +from . import ( + error, + util, + wireproto, +) class remotelock(object): def __init__(self, repo): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/sshserver.py --- a/mercurial/sshserver.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/sshserver.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,8 +6,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import util, hook, wireproto -import os, sys +from __future__ import absolute_import + +import os +import sys + +from . import ( + hook, + util, + wireproto, +) class sshserver(wireproto.abstractserverproto): def __init__(self, ui, repo): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/sslutil.py --- a/mercurial/sslutil.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/sslutil.py Tue Sep 01 17:09:00 2015 -0500 @@ -6,10 +6,15 @@ # # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import os, sys, ssl + +from __future__ import absolute_import -from mercurial import util -from mercurial.i18n import _ +import os +import ssl +import sys + +from .i18n import _ +from . import util _canloaddefaultcerts = False try: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/statichttprepo.py Tue Sep 01 17:09:00 2015 -0500 @@ -7,10 +7,26 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import changelog, byterange, url, error, namespaces -import localrepo, manifest, util, scmutil, store -import urllib, urllib2, errno, os +from __future__ import absolute_import + +import errno +import os +import urllib +import urllib2 + +from .i18n import _ +from . import ( + byterange, + changelog, + error, + localrepo, + manifest, + namespaces, + scmutil, + store, + url, + util, +) class httprangereader(object): def __init__(self, url, opener): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/strutil.py --- a/mercurial/strutil.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/strutil.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,6 +5,8 @@ # 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 + def findall(haystack, needle, start=0, end=None): if end is None: end = len(haystack) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/subrepo.py --- a/mercurial/subrepo.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/subrepo.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,15 +5,34 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import copy -import errno, os, re, posixpath, sys +import errno +import os +import posixpath +import re +import stat +import subprocess +import sys +import tarfile import xml.dom.minidom -import stat, subprocess, tarfile -from i18n import _ -import config, util, node, error, cmdutil, scmutil, match as matchmod -import phases -import pathutil -import exchange + + +from .i18n import _ +from . import ( + cmdutil, + config, + error, + exchange, + match as matchmod, + node, + pathutil, + phases, + scmutil, + util, +) + hg = None propertycache = util.propertycache @@ -328,7 +347,7 @@ # so we manually delay the circular imports to not break # scripts that don't use our demand-loading global hg - import hg as h + from . import hg as h hg = h pathutil.pathauditor(ctx.repo().root)(path) @@ -346,7 +365,7 @@ # so we manually delay the circular imports to not break # scripts that don't use our demand-loading global hg - import hg as h + from . import hg as h hg = h pathutil.pathauditor(ctx.repo().root)(path) diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/tagmerge.py --- a/mercurial/tagmerge.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/tagmerge.py Tue Sep 01 17:09:00 2015 -0500 @@ -71,11 +71,20 @@ # - put blocks whose nodes come all from p2 first # - write the tag blocks in the sorted order -import tags as tagsmod -import util -from node import nullid, hex -from i18n import _ +from __future__ import absolute_import + import operator + +from .i18n import _ +from .node import ( + hex, + nullid, +) +from .import ( + tags as tagsmod, + util, +) + hexnullid = hex(nullid) def readtagsformerge(ui, repo, lines, fn='', keeplinenums=False): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/tags.py --- a/mercurial/tags.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/tags.py Tue Sep 01 17:09:00 2015 -0500 @@ -10,15 +10,27 @@ # Eventually, it could take care of updating (adding/removing/moving) # tags too. -from node import nullid, bin, hex, short -from i18n import _ -import util -import encoding -import error -from array import array +from __future__ import absolute_import + +import array import errno import time +from .i18n import _ +from .node import ( + bin, + hex, + nullid, + short, +) +from . import ( + encoding, + error, + util, +) + +array = array.array + # Tags computation can be expensive and caches exist to make it fast in # the common case. # @@ -263,7 +275,7 @@ If the cache is not up to date, the caller is responsible for reading tag info from each returned head. (See findglobaltags().) ''' - import scmutil # avoid cycle + from . import scmutil # avoid cycle try: cachefile = repo.vfs(_filename(repo), 'r') diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/templatefilters.py --- a/mercurial/templatefilters.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/templatefilters.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,10 +5,21 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import cgi, re, os, time, urllib -import encoding, node, util -import hbisect -import templatekw +from __future__ import absolute_import + +import cgi +import os +import re +import time +import urllib + +from . import ( + encoding, + hbisect, + node, + templatekw, + util, +) def addbreaks(text): """:addbreaks: Any text. Add an XHTML "
" tag before the end of @@ -70,12 +81,6 @@ """:count: List or text. Returns the length as an integer.""" return len(i) -def datefilter(text): - """:date: Date. Returns a date in a Unix date format, including the - timezone: "Mon Sep 04 15:13:13 2006 0700". - """ - return util.datestr(text) - def domain(author): """:domain: Any text. Finds the first string that looks like an email address, and extracts just the domain component. Example: ``User @@ -230,10 +235,6 @@ s = s.replace(k, v) return ''.join(_uescape(c) for c in s) -def localdate(text): - """:localdate: Date. Converts a date to local date.""" - return (util.parsedate(text)[0], util.makedate()[1]) - def lower(text): """:lower: Any text. Converts the text to lowercase.""" return encoding.lower(text) @@ -337,10 +338,6 @@ return "" return str(thing) -def strip(text): - """:strip: Any text. Strips all leading and trailing whitespace.""" - return text.strip() - def stripdir(text): """:stripdir: Treat the text as path and strip a directory level, if possible. For example, "foo" and "foo/bar" becomes "foo". @@ -390,7 +387,6 @@ "age": age, "basename": basename, "count": count, - "date": datefilter, "domain": domain, "email": email, "escape": escape, @@ -403,7 +399,6 @@ "isodatesec": isodatesec, "json": json, "jsonescape": jsonescape, - "localdate": localdate, "lower": lower, "nonempty": nonempty, "obfuscate": obfuscate, @@ -418,7 +413,6 @@ "splitlines": splitlines, "stringescape": stringescape, "stringify": stringify, - "strip": strip, "stripdir": stripdir, "tabindent": tabindent, "upper": upper, diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/templatekw.py --- a/mercurial/templatekw.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/templatekw.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,9 +5,16 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import hex -import patch, scmutil, util, error -import hbisect +from __future__ import absolute_import + +from .node import hex +from . import ( + error, + hbisect, + patch, + scmutil, + util, +) # This helper class allows us to handle both: # "{files}" (legacy command-line-specific list hack) and diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/templater.py --- a/mercurial/templater.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/templater.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,12 +5,23 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import os, re -import util, config, templatefilters, templatekw, parser, error -import revset as revsetmod +from __future__ import absolute_import + +import os +import re import types -import minirst + +from .i18n import _ +from . import ( + config, + error, + minirst, + parser, + revset as revsetmod, + templatefilters, + templatekw, + util, +) # template parsing @@ -194,12 +205,6 @@ return getlist(x[1]) + [x[2]] return [x] -def getfilter(exp, context): - f = getsymbol(exp) - if f not in context._filters: - raise error.ParseError(_("unknown function '%s'") % f) - return context._filters[f] - def gettemplate(exp, context): if exp[0] == 'template': return [compileexp(e, context, methods) for e in exp[1]] @@ -210,6 +215,15 @@ return context._load(exp[1]) raise error.ParseError(_("expected template specifier")) +def evalfuncarg(context, mapping, arg): + func, data = arg + # func() may return string, generator of strings or arbitrary object such + # as date tuple, but filter does not want generator. + thing = func(context, mapping, data) + if isinstance(thing, types.GeneratorType): + thing = stringify(thing) + return thing + def runinteger(context, mapping, data): return int(data) @@ -242,24 +256,26 @@ yield func(context, mapping, data) def buildfilter(exp, context): - func, data = compileexp(exp[1], context, methods) - filt = getfilter(exp[2], context) - return (runfilter, (func, data, filt)) + arg = compileexp(exp[1], context, methods) + n = getsymbol(exp[2]) + if n in context._filters: + filt = context._filters[n] + return (runfilter, (arg, filt)) + if n in funcs: + f = funcs[n] + return (f, [arg]) + raise error.ParseError(_("unknown function '%s'") % n) def runfilter(context, mapping, data): - func, data, filt = data - # func() may return string, generator of strings or arbitrary object such - # as date tuple, but filter does not want generator. - thing = func(context, mapping, data) - if isinstance(thing, types.GeneratorType): - thing = stringify(thing) + arg, filt = data + thing = evalfuncarg(context, mapping, arg) try: return filt(thing) except (ValueError, AttributeError, TypeError): - if isinstance(data, tuple): - dt = data[1] + if isinstance(arg[1], tuple): + dt = arg[1][1] else: - dt = data + dt = arg[1] raise util.Abort(_("template filter '%s' is not compatible with " "keyword '%s'") % (filt.func_name, dt)) @@ -297,12 +313,13 @@ if len(args) != 1: raise error.ParseError(_("filter %s expects one argument") % n) f = context._filters[n] - return (runfilter, (args[0][0], args[0][1], f)) + return (runfilter, (args[0], f)) raise error.ParseError(_("unknown function '%s'") % n) def date(context, mapping, args): """:date(date[, fmt]): Format a date. See :hg:`help dates` for formatting - strings.""" + strings. The default is a Unix date format, including the timezone: + "Mon Sep 04 15:13:13 2006 0700".""" if not (1 <= len(args) <= 2): # i18n: "date" is a keyword raise error.ParseError(_("date expects one or two arguments")) @@ -499,6 +516,34 @@ # ignore args[0] (the label string) since this is supposed to be a a no-op yield args[1][0](context, mapping, args[1][1]) +def localdate(context, mapping, args): + """:localdate(date[, tz]): Converts a date to the specified timezone. + The default is local date.""" + if not (1 <= len(args) <= 2): + # i18n: "localdate" is a keyword + raise error.ParseError(_("localdate expects one or two arguments")) + + date = evalfuncarg(context, mapping, args[0]) + try: + date = util.parsedate(date) + except AttributeError: # not str nor date tuple + # i18n: "localdate" is a keyword + raise error.ParseError(_("localdate expects a date information")) + if len(args) >= 2: + tzoffset = None + tz = evalfuncarg(context, mapping, args[1]) + if isinstance(tz, str): + tzoffset = util.parsetimezone(tz) + if tzoffset is None: + try: + tzoffset = int(tz) + except (TypeError, ValueError): + # i18n: "localdate" is a keyword + raise error.ParseError(_("localdate expects a timezone")) + else: + tzoffset = util.makedate()[1] + return (date[0], tzoffset) + def revset(context, mapping, args): """:revset(query[, formatargs...]): Execute a revision set query. See :hg:`help revset`.""" @@ -593,7 +638,8 @@ return shortest def strip(context, mapping, args): - """:strip(text[, chars]): Strip characters from a string.""" + """:strip(text[, chars]): Strip characters from a string. By default, + strips all leading and trailing whitespace.""" if not (1 <= len(args) <= 2): # i18n: "strip" is a keyword raise error.ParseError(_("strip expects one or two arguments")) @@ -682,6 +728,7 @@ "indent": indent, "join": join, "label": label, + "localdate": localdate, "pad": pad, "revset": revset, "rstdoc": rstdoc, diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/templates/monoblue/footer.tmpl --- a/mercurial/templates/monoblue/footer.tmpl Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/templates/monoblue/footer.tmpl Tue Sep 01 17:09:00 2015 -0500 @@ -12,11 +12,6 @@

mercurial

-
-
-
-
- diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/templates/monoblue/index.tmpl --- a/mercurial/templates/monoblue/index.tmpl Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/templates/monoblue/index.tmpl Tue Sep 01 17:09:00 2015 -0500 @@ -29,11 +29,6 @@

mercurial

-
-
-
-
- diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/templates/monoblue/summary.tmpl --- a/mercurial/templates/monoblue/summary.tmpl Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/templates/monoblue/summary.tmpl Tue Sep 01 17:09:00 2015 -0500 @@ -26,6 +26,7 @@
  • bookmarks
  • branches
  • files
  • + {archives%archiveentry}
  • help
  • diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/templates/static/style-monoblue.css --- a/mercurial/templates/static/style-monoblue.css Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/templates/static/style-monoblue.css Tue Sep 01 17:09:00 2015 -0500 @@ -263,7 +263,6 @@ td.source { white-space: pre; - font-family: monospace; margin: 10px 30px 0; font-size: 1.2em; font-family: monospace; @@ -311,44 +310,6 @@ div#powered-by a:hover { text-decoration: underline; } -/* -div#monoblue-corner-top-left { - position: absolute; - top: 0; - left: 0; - width: 10px; - height: 10px; - background: url(./monoblue-corner.png) top left no-repeat !important; - background: none; -} -div#monoblue-corner-top-right { - position: absolute; - top: 0; - right: 0; - width: 10px; - height: 10px; - background: url(./monoblue-corner.png) top right no-repeat !important; - background: none; -} -div#monoblue-corner-bottom-left { - position: absolute; - bottom: 0; - left: 0; - width: 10px; - height: 10px; - background: url(./monoblue-corner.png) bottom left no-repeat !important; - background: none; -} -div#monoblue-corner-bottom-right { - position: absolute; - bottom: 0; - right: 0; - width: 10px; - height: 10px; - background: url(./monoblue-corner.png) bottom right no-repeat !important; - background: none; -} -*/ /** end of common settings **/ /** summary **/ diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/transaction.py --- a/mercurial/transaction.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/transaction.py Tue Sep 01 17:09:00 2015 -0500 @@ -11,9 +11,15 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ +from __future__ import absolute_import + import errno -import error, util + +from .i18n import _ +from . import ( + error, + util, +) version = 2 diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/treediscovery.py --- a/mercurial/treediscovery.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/treediscovery.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,10 +5,19 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + import collections -from node import nullid, short -from i18n import _ -import util, error + +from .i18n import _ +from .node import ( + nullid, + short, +) +from . import ( + error, + util, +) def findcommonincoming(repo, remote, heads=None, force=False): """Return a tuple (common, fetch, heads) used to identify the common diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/ui.py --- a/mercurial/ui.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/ui.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,11 +5,28 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. +from __future__ import absolute_import + +import errno +import getpass import inspect -from i18n import _ -import errno, getpass, os, socket, sys, tempfile, traceback -import config, scmutil, util, error, formatter, progress -from node import hex +import os +import socket +import sys +import tempfile +import traceback + +from .i18n import _ +from .node import hex + +from . import ( + config, + error, + formatter, + progress, + scmutil, + util, +) samplehgrcs = { 'user': @@ -533,12 +550,9 @@ def expandpath(self, loc, default=None): """Return repository location relative to cwd or from [paths]""" - if util.hasscheme(loc) or os.path.isdir(os.path.join(loc, '.hg')): - return loc - p = self.paths.getpath(loc, default=default) if p: - return p.loc + return p.rawloc return loc @util.propertycache @@ -982,10 +996,23 @@ # No location is the same as not existing. if not loc: continue + + # TODO ignore default-push once all consumers stop referencing it + # since it is handled specifically below. + self[name] = path(name, rawloc=loc) + # Handle default-push, which is a one-off that defines the push URL for + # the "default" path. + defaultpush = ui.config('paths', 'default-push') + if defaultpush and 'default' in self: + self['default']._pushloc = defaultpush + def getpath(self, name, default=None): - """Return a ``path`` for the specified name, falling back to a default. + """Return a ``path`` from a string, falling back to a default. + + ``name`` can be a named path or locations. Locations are filesystem + paths or URIs. Returns the first of ``name`` or ``default`` that is present, or None if neither is present. @@ -993,6 +1020,12 @@ try: return self[name] except KeyError: + # Try to resolve as a local path or URI. + try: + return path(None, rawloc=name) + except ValueError: + pass + if default is not None: try: return self[default] @@ -1004,15 +1037,51 @@ class path(object): """Represents an individual path and its configuration.""" - def __init__(self, name, rawloc=None): + def __init__(self, name, rawloc=None, pushloc=None): """Construct a path from its config options. ``name`` is the symbolic name of the path. ``rawloc`` is the raw location, as defined in the config. + ``pushloc`` is the raw locations pushes should be made to. + + If ``name`` is not defined, we require that the location be a) a local + filesystem path with a .hg directory or b) a URL. If not, + ``ValueError`` is raised. """ + if not rawloc: + raise ValueError('rawloc must be defined') + + # Locations may define branches via syntax #. + u = util.url(rawloc) + branch = None + if u.fragment: + branch = u.fragment + u.fragment = None + + self.url = u + self.branch = branch + self.name = name - # We'll do more intelligent things with rawloc in the future. - self.loc = rawloc + self.rawloc = rawloc + self.loc = str(u) + self._pushloc = pushloc + + # When given a raw location but not a symbolic name, validate the + # location is valid. + if not name and not u.scheme and not self._isvalidlocalpath(self.loc): + raise ValueError('location is not a URL or path to a local ' + 'repo: %s' % rawloc) + + def _isvalidlocalpath(self, path): + """Returns True if the given path is a potentially valid repository. + This is its own function so that extensions can change the definition of + 'valid' in this case (like when pulling from a git repo into a hg + one).""" + return os.path.isdir(os.path.join(path, '.hg')) + + @property + def pushloc(self): + return self._pushloc or self.loc # we instantiate one globally shared progress bar to avoid # competing progress bars when multiple UI objects get created diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/unionrepo.py --- a/mercurial/unionrepo.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/unionrepo.py Tue Sep 01 17:09:00 2015 -0500 @@ -11,11 +11,25 @@ allowing operations like diff and log with revsets. """ -from node import nullid -from i18n import _ +from __future__ import absolute_import + import os -import util, mdiff, cmdutil, scmutil -import localrepo, changelog, manifest, filelog, revlog, pathutil + +from .i18n import _ +from .node import nullid + +from . import ( + changelog, + cmdutil, + filelog, + localrepo, + manifest, + mdiff, + pathutil, + revlog, + scmutil, + util, +) class unionrevlog(revlog.revlog): def __init__(self, opener, indexfile, revlog2, linkmapper): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/url.py --- a/mercurial/url.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/url.py Tue Sep 01 17:09:00 2015 -0500 @@ -7,10 +7,23 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import urllib, urllib2, httplib, os, socket, cStringIO, base64 -from i18n import _ -import keepalive, util, sslutil -import httpconnection as httpconnectionmod +from __future__ import absolute_import + +import base64 +import cStringIO +import httplib +import os +import socket +import urllib +import urllib2 + +from .i18n import _ +from . import ( + httpconnection as httpconnectionmod, + keepalive, + sslutil, + util, +) class passwordmgr(urllib2.HTTPPasswordMgrWithDefaultRealm): def __init__(self, ui): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/util.py --- a/mercurial/util.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/util.py Tue Sep 01 17:09:00 2015 -0500 @@ -1371,22 +1371,22 @@ """turn (timestamp, tzoff) tuple into iso 8631 date.""" return datestr(date, format='%Y-%m-%d') +def parsetimezone(tz): + """parse a timezone string and return an offset integer""" + if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit(): + sign = (tz[0] == "+") and 1 or -1 + hours = int(tz[1:3]) + minutes = int(tz[3:5]) + return -sign * (hours * 60 + minutes) * 60 + if tz == "GMT" or tz == "UTC": + return 0 + return None + def strdate(string, format, defaults=[]): """parse a localized time string and return a (unixtime, offset) tuple. if the string cannot be parsed, ValueError is raised.""" - def timezone(string): - tz = string.split()[-1] - if tz[0] in "+-" and len(tz) == 5 and tz[1:].isdigit(): - sign = (tz[0] == "+") and 1 or -1 - hours = int(tz[1:3]) - minutes = int(tz[3:5]) - return -sign * (hours * 60 + minutes) * 60 - if tz == "GMT" or tz == "UTC": - return 0 - return None - # NOTE: unixtime = localunixtime + offset - offset, date = timezone(string), string + offset, date = parsetimezone(string.split()[-1]), string if offset is not None: date = " ".join(string.split()[:-1]) @@ -2260,7 +2260,7 @@ class hooks(object): '''A collection of hook functions that can be used to extend a - function's behaviour. Hooks are called in lexicographic order, + function's behavior. Hooks are called in lexicographic order, based on the names of their sources.''' def __init__(self): diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/verify.py --- a/mercurial/verify.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/verify.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,10 +5,21 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from node import nullid, short -from i18n import _ +from __future__ import absolute_import + import os -import revlog, util, error + +from .i18n import _ +from .node import ( + nullid, + short, +) + +from . import ( + error, + revlog, + util, +) def verify(repo): lock = repo.lock() diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/win32.py --- a/mercurial/win32.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/win32.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,7 +5,14 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import ctypes, errno, msvcrt, os, subprocess, random +from __future__ import absolute_import + +import ctypes +import errno +import msvcrt +import os +import random +import subprocess _kernel32 = ctypes.windll.kernel32 _advapi32 = ctypes.windll.advapi32 diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/wireproto.py --- a/mercurial/wireproto.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/wireproto.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,12 +5,29 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -import urllib, tempfile, os, sys -from i18n import _ -from node import bin, hex -import changegroup as changegroupmod, bundle2, pushkey as pushkeymod -import peer, error, encoding, util, exchange +from __future__ import absolute_import + +import os +import sys +import tempfile +import urllib +from .i18n import _ +from .node import ( + bin, + hex, +) + +from . import ( + bundle2, + changegroup as changegroupmod, + encoding, + error, + exchange, + peer, + pushkey as pushkeymod, + util, +) class abstractserverproto(object): """abstract class that summarizes the protocol API @@ -58,48 +75,12 @@ Some protocols may have compressed the contents.""" raise NotImplementedError() -# abstract batching support - -class future(object): - '''placeholder for a value to be set later''' - def set(self, value): - if util.safehasattr(self, 'value'): - raise error.RepoError("future is already set") - self.value = value - -class batcher(object): - '''base class for batches of commands submittable in a single request - - All methods invoked on instances of this class are simply queued and - return a a future for the result. Once you call submit(), all the queued - calls are performed and the results set in their respective futures. - ''' - def __init__(self): - self.calls = [] - def __getattr__(self, name): - def call(*args, **opts): - resref = future() - self.calls.append((name, args, opts, resref,)) - return resref - return call - def submit(self): - pass - -class localbatch(batcher): - '''performs the queued calls directly''' - def __init__(self, local): - batcher.__init__(self) - self.local = local - def submit(self): - for name, args, opts, resref in self.calls: - resref.set(getattr(self.local, name)(*args, **opts)) - -class remotebatch(batcher): +class remotebatch(peer.batcher): '''batches the queued calls; uses as few roundtrips as possible''' def __init__(self, remote): '''remote must support _submitbatch(encbatch) and _submitone(op, encargs)''' - batcher.__init__(self) + peer.batcher.__init__(self) self.remote = remote def submit(self): req, rsp = [], [] @@ -128,41 +109,10 @@ encresref.set(encres) resref.set(batchable.next()) -def batchable(f): - '''annotation for batchable methods - - Such methods must implement a coroutine as follows: - - @batchable - def sample(self, one, two=None): - # Handle locally computable results first: - if not one: - yield "a local result", None - # Build list of encoded arguments suitable for your wire protocol: - encargs = [('one', encode(one),), ('two', encode(two),)] - # Create future for injection of encoded result: - encresref = future() - # Return encoded arguments and future: - yield encargs, encresref - # Assuming the future to be filled with the result from the batched - # request now. Decode it: - yield decode(encresref.value) - - The decorator returns a function which wraps this coroutine as a plain - method, but adds the original method as an attribute called "batchable", - which is used by remotebatch to split the call into separate encoding and - decoding phases. - ''' - def plain(*args, **opts): - batchable = f(*args, **opts) - encargsorres, encresref = batchable.next() - if not encresref: - return encargsorres # a local result in this case - self = args[0] - encresref.set(self._submitone(f.func_name, encargsorres)) - return batchable.next() - setattr(plain, 'batchable', f) - return plain +# Forward a couple of names from peer to make wireproto interactions +# slightly more sensible. +batchable = peer.batchable +future = peer.future # list of nodes encoding / decoding @@ -216,7 +166,10 @@ class wirepeer(peer.peerrepository): def batch(self): - return remotebatch(self) + if self.capable('batch'): + return remotebatch(self) + else: + return peer.localbatch(self) def _submitbatch(self, req): cmds = [] for op, argsdict in req: diff -r d0843f7e16b4 -r f77a3f27cea5 mercurial/worker.py --- a/mercurial/worker.py Tue Sep 01 16:38:33 2015 -0500 +++ b/mercurial/worker.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,30 +5,24 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from i18n import _ -import errno, os, signal, sys, threading -import util +from __future__ import absolute_import + +import errno +import multiprocessing +import os +import signal +import sys +import threading + +from .i18n import _ +from . import util def countcpus(): '''try to count the number of CPUs on the system''' - - # posix try: - n = int(os.sysconf('SC_NPROCESSORS_ONLN')) - if n > 0: - return n - except (AttributeError, ValueError): - pass - - # windows - try: - n = int(os.environ['NUMBER_OF_PROCESSORS']) - if n > 0: - return n - except (KeyError, ValueError): - pass - - return 1 + return multiprocessing.cpu_count() + except NotImplementedError: + return 1 def _numworkers(ui): s = ui.config('worker', 'numcpus') diff -r d0843f7e16b4 -r f77a3f27cea5 tests/bzr-definitions --- a/tests/bzr-definitions Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/bzr-definitions Tue Sep 01 17:09:00 2015 -0500 @@ -1,7 +1,5 @@ # this file holds the definitions that are used in various bzr tests -"$TESTDIR/hghave" bzr || exit 80 - TERM=dumb; export TERM echo '[extensions]' >> $HGRCPATH echo 'convert = ' >> $HGRCPATH diff -r d0843f7e16b4 -r f77a3f27cea5 tests/filterpyflakes.py --- a/tests/filterpyflakes.py Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/filterpyflakes.py Tue Sep 01 17:09:00 2015 -0500 @@ -2,7 +2,7 @@ # Filter output by pyflakes to control which warnings we check -import sys, re, os +import sys, re def makekey(typeandline): """ @@ -42,7 +42,7 @@ else: continue # no pattern matched, next line fn = line.split(':', 1)[0] - f = open(os.path.join(os.path.dirname(os.path.dirname(__file__)), fn)) + f = open(fn) data = f.read() f.close() if 'no-' 'check-code' in data: diff -r d0843f7e16b4 -r f77a3f27cea5 tests/hghave --- a/tests/hghave Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/hghave Tue Sep 01 17:09:00 2015 -0500 @@ -30,10 +30,8 @@ help="test available features") parser.add_option("--list-features", action="store_true", help="list available features") -parser.add_option("-q", "--quiet", action="store_true", - help="check features silently") -def _loadaddon(quiet): +def _loadaddon(): if 'TESTDIR' in os.environ: # loading from '.' isn't needed, because `hghave` should be # running at TESTTMP in this case @@ -48,15 +46,14 @@ try: import hghaveaddon except BaseException, inst: - if not quiet: - sys.stderr.write('failed to import hghaveaddon.py from %r: %s\n' - % (path, inst)) + sys.stderr.write('failed to import hghaveaddon.py from %r: %s\n' + % (path, inst)) sys.exit(2) sys.path.pop(0) if __name__ == '__main__': options, args = parser.parse_args() - _loadaddon(options.quiet) + _loadaddon() if options.list_features: list_features() sys.exit(0) @@ -64,36 +61,4 @@ if options.test_features: sys.exit(test_features()) - quiet = options.quiet - - failures = 0 - - def error(msg): - global failures - if not quiet: - sys.stderr.write(msg + '\n') - failures += 1 - - for feature in args: - negate = feature.startswith('no-') - if negate: - feature = feature[3:] - - if feature not in checks: - error('skipped: unknown feature: ' + feature) - sys.exit(2) - - check, desc = checks[feature] - try: - available = check() - except Exception, e: - error('hghave check failed: ' + feature) - continue - - if not negate and not available: - error('skipped: missing feature: ' + desc) - elif negate and available: - error('skipped: system supports %s' % desc) - - if failures != 0: - sys.exit(1) + hghave.require(args) diff -r d0843f7e16b4 -r f77a3f27cea5 tests/hghave.py --- a/tests/hghave.py Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/hghave.py Tue Sep 01 17:09:00 2015 -0500 @@ -1,6 +1,9 @@ -import os, stat +import errno +import os import re import socket +import stat +import subprocess import sys import tempfile @@ -17,19 +20,68 @@ return func return decorator +def checkfeatures(features): + result = { + 'error': [], + 'missing': [], + 'skipped': [], + } + + for feature in features: + negate = feature.startswith('no-') + if negate: + feature = feature[3:] + + if feature not in checks: + result['missing'].append(feature) + continue + + check, desc = checks[feature] + try: + available = check() + except Exception: + result['error'].append('hghave check failed: %s' % feature) + continue + + if not negate and not available: + result['skipped'].append('missing feature: %s' % desc) + elif negate and available: + result['skipped'].append('system supports %s' % desc) + + return result + +def require(features): + """Require that features are available, exiting if not.""" + result = checkfeatures(features) + + for missing in result['missing']: + sys.stderr.write('skipped: unknown feature: %s\n' % missing) + for msg in result['skipped']: + sys.stderr.write('skipped: %s\n' % msg) + for msg in result['error']: + sys.stderr.write('%s\n' % msg) + + if result['missing']: + sys.exit(2) + + if result['skipped'] or result['error']: + sys.exit(1) + def matchoutput(cmd, regexp, ignorestatus=False): """Return True if cmd executes successfully and its output is matched by the supplied regular expression. """ r = re.compile(regexp) - fh = os.popen(cmd) - s = fh.read() try: - ret = fh.close() - except IOError: - # Happen in Windows test environment - ret = 1 - return (ignorestatus or ret is None) and r.search(s) + p = subprocess.Popen( + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + except OSError as e: + if e.errno != errno.ENOENT: + raise + ret = -1 + ret = p.wait() + s = p.stdout.read() + return (ignorestatus or not ret) and r.search(s) @check("baz", "GNU Arch baz client") def has_baz(): @@ -367,6 +419,33 @@ def has_osx(): return sys.platform == 'darwin' +@check("docker", "docker support") +def has_docker(): + pat = r'A self-sufficient runtime for linux containers\.' + if matchoutput('docker --help', pat): + if 'linux' not in sys.platform: + # TODO: in theory we should be able to test docker-based + # package creation on non-linux using boot2docker, but in + # practice that requires extra coordination to make sure + # $TESTTEMP is going to be visible at the same path to the + # boot2docker VM. If we figure out how to verify that, we + # can use the following instead of just saying False: + # return 'DOCKER_HOST' in os.environ + return False + + return True + return False + +@check("debhelper", "debian packaging tools") +def has_debhelper(): + dpkg = matchoutput('dpkg --version', + "Debian `dpkg' package management program") + dh = matchoutput('dh --help', + 'dh is a part of debhelper.') + dh_py2 = matchoutput('dh_python2 --help', + 'other supported Python versions') + return dpkg and dh and dh_py2 + @check("absimport", "absolute_import in __future__") def has_absimport(): import __future__ @@ -380,3 +459,7 @@ @check("pure", "running with pure Python code") def has_pure(): return os.environ.get("HGTEST_RUN_TESTS_PURE") == "--pure" + +@check("slow", "allow slow tests") +def has_slow(): + return os.environ.get('HGTEST_SLOW') == 'slow' diff -r d0843f7e16b4 -r f77a3f27cea5 tests/run-tests.py --- a/tests/run-tests.py Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/run-tests.py Tue Sep 01 17:09:00 2015 -0500 @@ -259,6 +259,8 @@ help='run tests in random order') parser.add_option('--profile-runner', action='store_true', help='run statprof on run-tests') + parser.add_option('--allow-slow-tests', action='store_true', + help='allow extremely slow tests') for option, (envvar, default) in defaults.items(): defaults[option] = type(default)(os.environ.get(envvar, default)) @@ -1835,6 +1837,11 @@ if self.options.pure: os.environ["HGTEST_RUN_TESTS_PURE"] = "--pure" + if self.options.allow_slow_tests: + os.environ["HGTEST_SLOW"] = "slow" + elif 'HGTEST_SLOW' in os.environ: + del os.environ['HGTEST_SLOW'] + self._coveragefile = os.path.join(self._testdir, b'.coverage') vlog("# Using TESTDIR", self._testdir) @@ -2078,7 +2085,11 @@ vlog("# Running", cmd) if os.system(cmd) == 0: if not self.options.verbose: - os.remove(installerrs) + try: + os.remove(installerrs) + except OSError as e: + if e.errno != errno.ENOENT: + raise else: f = open(installerrs, 'rb') for line in f: diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-batching.py --- a/tests/test-batching.py Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-batching.py Tue Sep 01 17:09:00 2015 -0500 @@ -5,7 +5,8 @@ # This software may be used and distributed according to the terms of the # GNU General Public License version 2 or any later version. -from mercurial.wireproto import localbatch, remotebatch, batchable, future +from mercurial.peer import localbatch, batchable, future +from mercurial.wireproto import remotebatch # equivalent of repo.repository class thing(object): diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-bookmarks.t --- a/tests/test-bookmarks.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-bookmarks.t Tue Sep 01 17:09:00 2015 -0500 @@ -511,10 +511,10 @@ test clone with update to a bookmark - $ hg clone -u Z . cloned-bookmarks-update + $ hg clone -u Z . ../cloned-bookmarks-update updating to branch default 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg -R cloned-bookmarks-update bookmarks + $ hg -R ../cloned-bookmarks-update bookmarks X2 1:925d80f479bb Y 2:db815d6d32e6 * Z 2:db815d6d32e6 @@ -569,10 +569,10 @@ $ hg bookmark -r3 Y moving bookmark 'Y' forward from db815d6d32e6 - $ hg -R cloned-bookmarks-update update Y + $ hg -R ../cloned-bookmarks-update update Y 0 files updated, 0 files merged, 0 files removed, 0 files unresolved (activating bookmark Y) - $ hg -R cloned-bookmarks-update pull --update . + $ hg -R ../cloned-bookmarks-update pull --update . pulling from . searching for changes adding changesets diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-clone-r.t --- a/tests/test-clone-r.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-clone-r.t Tue Sep 01 17:09:00 2015 -0500 @@ -218,3 +218,26 @@ 4 files, 9 changesets, 7 total revisions $ cd .. + $ hg clone test test-9 + updating to branch default + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + $ cd test-9 + $ hg branch foobar + marked working directory as branch foobar + (branches are permanent and global, did you want a bookmark?) + $ echo file2 >> file2 + $ hg add file2 + $ hg commit -m "changeset9" + $ echo file3 >> file3 + $ hg add file3 + $ hg commit -m "changeset10" + $ cd .. + $ hg clone -r 9 -u foobar test-9 test-10 + adding changesets + adding manifests + adding file changes + added 6 changesets with 6 changes to 3 files + updating to branch foobar + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + + diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-command-template.t --- a/tests/test-command-template.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-command-template.t Tue Sep 01 17:09:00 2015 -0500 @@ -2495,10 +2495,14 @@ abort: template filter 'escape' is not compatible with keyword 'date' [255] + $ hg log -l 3 --template 'line: {extras|localdate}\n' + hg: parse error: localdate expects a date information + [255] + Behind the scenes, this will throw ValueError $ hg tip --template '{author|email|date}\n' - abort: template filter 'datefilter' is not compatible with keyword 'author' + hg: parse error: date expects a date information [255] Error in nested template: @@ -3105,6 +3109,25 @@ hg: parse error: get() expects a dict as first argument [255] +Test localdate(date, tz) function: + + $ TZ=JST-09 hg log -r0 -T '{date|localdate|isodate}\n' + 1970-01-01 09:00 +0900 + $ TZ=JST-09 hg log -r0 -T '{localdate(date, "UTC")|isodate}\n' + 1970-01-01 00:00 +0000 + $ TZ=JST-09 hg log -r0 -T '{localdate(date, "+0200")|isodate}\n' + 1970-01-01 02:00 +0200 + $ TZ=JST-09 hg log -r0 -T '{localdate(date, "0")|isodate}\n' + 1970-01-01 00:00 +0000 + $ TZ=JST-09 hg log -r0 -T '{localdate(date, 0)|isodate}\n' + 1970-01-01 00:00 +0000 + $ hg log -r0 -T '{localdate(date, "invalid")|isodate}\n' + hg: parse error: localdate expects a timezone + [255] + $ hg log -r0 -T '{localdate(date, date)|isodate}\n' + hg: parse error: localdate expects a timezone + [255] + Test shortest(node) function: $ echo b > b @@ -3117,6 +3140,8 @@ e777603221 bcc7ff960b f7769ec2ab + $ hg log --template '{node|shortest}\n' -l1 + e777 Test pad function diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-completion.t --- a/tests/test-completion.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-completion.t Tue Sep 01 17:09:00 2015 -0500 @@ -254,7 +254,7 @@ debugpathcomplete: full, normal, added, removed debugpushkey: debugpvec: - debugrebuilddirstate: rev + debugrebuilddirstate: rev, minimal debugrebuildfncache: debugrename: rev debugrevlog: changelog, manifest, dir, dump diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-convert-bzr-114.t --- a/tests/test-convert-bzr-114.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-convert-bzr-114.t Tue Sep 01 17:09:00 2015 -0500 @@ -1,4 +1,4 @@ -#require bzr114 +#require bzr bzr114 $ . "$TESTDIR/bzr-definitions" diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-convert-bzr-directories.t --- a/tests/test-convert-bzr-directories.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-convert-bzr-directories.t Tue Sep 01 17:09:00 2015 -0500 @@ -1,3 +1,4 @@ +#require bzr $ . "$TESTDIR/bzr-definitions" diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-convert-bzr-ghosts.t --- a/tests/test-convert-bzr-ghosts.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-convert-bzr-ghosts.t Tue Sep 01 17:09:00 2015 -0500 @@ -1,3 +1,4 @@ +#require bzr $ . "$TESTDIR/bzr-definitions" $ cat > ghostcreator.py < treeset.py < a && hg commit -Aqm 'edit a' + $ hg up -q 0 + $ touch b && hg commit -Aqm 'add b' + $ hg merge -q 1 && hg commit -qm 'merge a & b' + + $ cd .. + $ hg init merge-test2 + $ cd merge-test2 + $ mkdir converted + $ touch converted/a toberemoved && hg commit -Aqm 'add converted/a & toberemoved' + $ touch x && rm toberemoved && hg commit -Aqm 'add x & remove tobremoved' + $ cd .. + $ hg log -G -T '{shortest(node)} {desc}' -R merge-test1 + @ 1191 merge a & b + |\ + | o 9077 add b + | | + o | d19f edit a + |/ + o ac82 add a + + $ hg log -G -T '{shortest(node)} {desc}' -R merge-test2 + @ 150e add x & remove tobremoved + | + o bbac add converted/a & toberemoved + +- Build a shamap where the target converted/a is in on top of an unrelated +- change to 'x'. This simulates using convert to merge several repositories +- together. + $ cat >> merge-test2/.hg/shamap < $(hg -R merge-test1 log -r 0 -T '{node}') $(hg -R merge-test2 log -r 0 -T '{node}') + > $(hg -R merge-test1 log -r 1 -T '{node}') $(hg -R merge-test2 log -r 1 -T '{node}') + > EOF + $ cat >> merge-test-filemap < rename . converted/ + > EOF + $ hg convert --filemap merge-test-filemap merge-test1 merge-test2 --traceback + scanning source... + sorting... + converting... + 1 add b + 0 merge a & b + $ hg -R merge-test2 manifest -r tip + converted/a + converted/b + x + $ hg -R merge-test2 log -G -T '{shortest(node)} {desc}\n{files % "- {file}\n"}\n' + o 6eaa merge a & b + |\ - converted/a + | | - toberemoved + | | + | o 2995 add b + | | - converted/b + | | + @ | 150e add x & remove tobremoved + |/ - toberemoved + | - x + | + o bbac add converted/a & toberemoved + - converted/a + - toberemoved + + diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-convert-git.t --- a/tests/test-convert-git.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-convert-git.t Tue Sep 01 17:09:00 2015 -0500 @@ -652,6 +652,12 @@ $ hg -R git-repo6-hg tip -T "{file_dels}\n" .hgsub .hgsubstate +skip submodules in the conversion + + $ hg convert -q git-repo6 no-submodules --config convert.git.skipsubmodules=True + $ hg -R no-submodules manifest --all + .gitmodules-renamed + convert using a different remote prefix $ git init git-repo7 Initialized empty Git repository in $TESTTMP/git-repo7/.git/ @@ -678,6 +684,28 @@ master 0:03bf38caa4c6 origin/master 0:03bf38caa4c6 +Run convert when the remote branches have changed +(there was an old bug where the local convert read branches from the server) + + $ cd git-repo7 + $ echo a >> a + $ git commit -am "move master forward" + [master 0c81947] move master forward + Author: nottest + 1 file changed, 1 insertion(+) + $ cd .. + $ rm -rf hg-repo7 + $ hg convert --config convert.git.remoteprefix=origin git-repo7-client hg-repo7 + initializing destination hg-repo7 repository + scanning source... + sorting... + converting... + 0 commit a + updating bookmarks + $ hg -R hg-repo7 bookmarks + master 0:03bf38caa4c6 + origin/master 0:03bf38caa4c6 + damaged git repository tests: In case the hard-coded hashes change, the following commands can be used to list the hashes and their corresponding types in the repository: diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-convert.t --- a/tests/test-convert.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-convert.t Tue Sep 01 17:09:00 2015 -0500 @@ -265,6 +265,9 @@ remote refs are converted as bookmarks with "convert.git.remoteprefix" as a prefix followed by a /. The default is 'remote'. + convert.git.skipsubmodules + does not convert root level .gitmodules files or files with + 160000 mode indicating a submodule. Default is False. Perforce Source ############### diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-copy-move-merge.t --- a/tests/test-copy-move-merge.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-copy-move-merge.t Tue Sep 01 17:09:00 2015 -0500 @@ -59,4 +59,107 @@ 1 2 +Test disabling copy tracing + +- first verify copy metadata was kept + + $ hg up -qC 2 + $ hg rebase --keep -d 1 -b 2 --config extensions.rebase= + rebasing 2:add3f11052fa "other" (tip) + merging b and a to b + merging c and a to c + + $ cat b + 0 + 1 + 2 + +- next verify copy metadata is lost when disabled + + $ hg strip -r . --config extensions.strip= + 2 files updated, 0 files merged, 0 files removed, 0 files unresolved + saved backup bundle to $TESTTMP/t/.hg/strip-backup/550bd84c0cd3-fc575957-backup.hg (glob) + $ hg up -qC 2 + $ hg rebase --keep -d 1 -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True + rebasing 2:add3f11052fa "other" (tip) + remote changed a which local deleted + use (c)hanged version or leave (d)eleted? c + + $ cat b + 1 + 2 + $ cd .. + +Verify disabling copy tracing still keeps copies from rebase source + + $ hg init copydisable + $ cd copydisable + $ touch a + $ hg ci -Aqm 'add a' + $ touch b + $ hg ci -Aqm 'add b, c' + $ hg cp b x + $ echo x >> x + $ hg ci -qm 'copy b->x' + $ hg up -q 1 + $ touch z + $ hg ci -Aqm 'add z' + $ hg log -G -T '{rev} {desc}\n' + @ 3 add z + | + | o 2 copy b->x + |/ + o 1 add b, c + | + o 0 add a + + $ hg rebase -d . -b 2 --config extensions.rebase= --config experimental.disablecopytrace=True + rebasing 2:6adcf8c12e7d "copy b->x" + saved backup bundle to $TESTTMP/copydisable/.hg/strip-backup/6adcf8c12e7d-ce4b3e75-backup.hg (glob) + $ hg up -q 3 + $ hg log -f x -T '{rev} {desc}\n' + 3 copy b->x + 1 add b, c + + $ cd ../ + +Verify we duplicate existing copies, instead of detecting them + + $ hg init copydisable3 + $ cd copydisable3 + $ touch a + $ hg ci -Aqm 'add a' + $ hg cp a b + $ hg ci -Aqm 'copy a->b' + $ hg mv b c + $ hg ci -Aqm 'move b->c' + $ hg up -q 0 + $ hg cp a b + $ echo b >> b + $ hg ci -Aqm 'copy a->b (2)' + $ hg log -G -T '{rev} {desc}\n' + @ 3 copy a->b (2) + | + | o 2 move b->c + | | + | o 1 copy a->b + |/ + o 0 add a + + $ hg rebase -d 2 -s 3 --config extensions.rebase= --config experimental.disablecopytrace=True + rebasing 3:47e1a9e6273b "copy a->b (2)" (tip) + saved backup bundle to $TESTTMP/copydisable3/.hg/strip-backup/47e1a9e6273b-2d099c59-backup.hg (glob) + + $ hg log -G -f b + @ changeset: 3:76024fb4b05b + | tag: tip + | user: test + | date: Thu Jan 01 00:00:00 1970 +0000 + | summary: copy a->b (2) + | + o changeset: 0:ac82d8b1f7c4 + user: test + date: Thu Jan 01 00:00:00 1970 +0000 + summary: add a + diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-default-push.t --- a/tests/test-default-push.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-default-push.t Tue Sep 01 17:09:00 2015 -0500 @@ -18,7 +18,6 @@ Push should provide a hint when both 'default' and 'default-push' not set: $ cd c $ hg push --config paths.default= - pushing to default-push abort: default repository not configured! (see the "path" section in "hg help config") [255] diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-diff-change.t --- a/tests/test-diff-change.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-diff-change.t Tue Sep 01 17:09:00 2015 -0500 @@ -29,15 +29,59 @@ -first +second -Test dumb revspecs (issue3474) + $ cd .. + +Test dumb revspecs: top-level "x:y", "x:", ":y" and ":" ranges should be handled +as pairs even if x == y, but not for "f(x:y)" nor "x::y" (issue3474, issue4774) + + $ hg clone -q a dumbspec + $ cd dumbspec + $ echo "wdir" > file.txt $ hg diff -r 2:2 + $ hg diff -r 2:. + $ hg diff -r 2: + $ hg diff -r :0 + $ hg diff -r '2:first(2:2)' + $ hg diff -r 'first(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 + +++ b/file.txt + @@ -1,1 +1,1 @@ + -third + +wdir $ hg diff -r "2 and 1" abort: empty revision range [255] + $ cd .. + + $ hg clone -qr0 a dumbspec-rev0 + $ cd dumbspec-rev0 + $ echo "wdir" > file.txt + + $ hg diff -r : + $ hg diff -r 'first(:)' --nodates + diff -r 4bb65dda5db4 file.txt + --- a/file.txt + +++ b/file.txt + @@ -1,1 +1,1 @@ + -first + +wdir + + $ cd .. + Testing diff --change when merge: + $ cd a + $ for i in 1 2 3 4 5 6 7 8 9 10; do > echo $i >> file.txt > done diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-filecache.py --- a/tests/test-filecache.py Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-filecache.py Tue Sep 01 17:09:00 2015 -0500 @@ -130,7 +130,7 @@ util.cachestat.__init__ = originit def test_filecache_synced(): - # test old behaviour that caused filecached properties to go out of sync + # test old behavior that caused filecached properties to go out of sync os.system('hg init && echo a >> a && hg ci -qAm.') repo = hg.repository(ui.ui()) # first rollback clears the filecache, but changelog to stays in __dict__ diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-filelog.py --- a/tests/test-filelog.py Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-filelog.py Tue Sep 01 17:09:00 2015 -0500 @@ -1,6 +1,6 @@ #!/usr/bin/env python """ -Tests the behaviour of filelog w.r.t. data starting with '\1\n' +Tests the behavior of filelog w.r.t. data starting with '\1\n' """ from mercurial import ui, hg from mercurial.node import nullid, hex diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-generaldelta.t --- a/tests/test-generaldelta.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-generaldelta.t Tue Sep 01 17:09:00 2015 -0500 @@ -69,3 +69,37 @@ rev offset length base linkrev nodeid p1 p2 0 0 3 0 1 1406e7411862 000000000000 000000000000 + $ cd .. + +Test format.aggressivemergedeltas + + $ hg init --config format.generaldelta=1 aggressive + $ cd aggressive + $ touch a b c d e + $ hg commit -Aqm side1 + $ hg up -q null + $ touch x y + $ hg commit -Aqm side2 + +- Verify non-aggressive merge uses p1 (commit 1) as delta parent + $ hg merge -q 0 + $ hg commit -q -m merge + $ hg debugindex -m + rev offset length delta linkrev nodeid p1 p2 + 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000 + 1 59 59 -1 1 315c023f341d 000000000000 000000000000 + 2 118 65 1 2 2ab389a983eb 315c023f341d 8dde941edb6e + + $ hg strip -q -r . --config extensions.strip= + +- Verify aggressive merge uses p2 (commit 0) as delta parent + $ hg up -q -C 1 + $ hg merge -q 0 + $ hg commit -q -m merge --config format.aggressivemergedeltas=True + $ hg debugindex -m + rev offset length delta linkrev nodeid p1 p2 + 0 0 59 -1 0 8dde941edb6e 000000000000 000000000000 + 1 59 59 -1 1 315c023f341d 000000000000 000000000000 + 2 118 62 0 2 2ab389a983eb 315c023f341d 8dde941edb6e + + $ cd .. diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-help.t --- a/tests/test-help.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-help.t Tue Sep 01 17:09:00 2015 -0500 @@ -912,6 +912,16 @@ working directory is checked out, it is equivalent to null. If an uncommitted merge is in progress, "." is the revision of the first parent. +Test repeated config section name + + $ hg help config.host + "http_proxy.host" + Host name and (optional) port of the proxy server, for example + "myproxy:8000". + + "smtp.host" + Host name of mail server, e.g. "mail.example.com". + Test templating help $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) ' @@ -1202,6 +1212,11 @@ ":tagmerge" Uses the internal tag merge algorithm (experimental). + ":union" + Uses the internal non-interactive simple merge algorithm for merging + files. It will use both left and right sides for conflict regions. No + markers are inserted. + Internal tools are always available and do not require a GUI but will by default not handle symlinks or binary files. diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-hgweb-commands.t --- a/tests/test-hgweb-commands.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-hgweb-commands.t Tue Sep 01 17:09:00 2015 -0500 @@ -1653,7 +1653,7 @@ Thu, 01 Jan 1970 00:00:00 +0000 cad8025a2e87 - unstable + unstable changeset | changelog | @@ -1663,7 +1663,7 @@ Thu, 01 Jan 1970 00:00:00 +0000 1d22e65f027e - stable + stable changeset | changelog | @@ -1673,7 +1673,7 @@ Thu, 01 Jan 1970 00:00:00 +0000 a4f92ed23982 - default + default changeset | changelog | diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-hgweb-descend-empties.t --- a/tests/test-hgweb-descend-empties.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-hgweb-descend-empties.t Tue Sep 01 17:09:00 2015 -0500 @@ -373,11 +373,6 @@

    mercurial

    -
    -
    -
    -
    - diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-hgweb-symrev.t --- a/tests/test-hgweb-symrev.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-hgweb-symrev.t Tue Sep 01 17:09:00 2015 -0500 @@ -639,6 +639,7 @@ (De)referencing symbolic revisions (monoblue) $ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT 'summary?style=monoblue' | egrep $REVLINKS +
  • zip
  • changeset | files diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-hgwebdir.t --- a/tests/test-hgwebdir.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-hgwebdir.t Tue Sep 01 17:09:00 2015 -0500 @@ -1245,6 +1245,67 @@ $ get-with-headers.py localhost:$HGPORT2 'a/rss-log' | grep 'http://hg.example.com:8080/foo/a/rev/8580ff50825a +Path refreshing works as expected + + $ killdaemons.py + $ mkdir $root/refreshtest + $ hg init $root/refreshtest/a + $ cat > paths.conf << EOF + > [paths] + > / = $root/refreshtest/* + > EOF + $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf + $ cat hg.pid >> $DAEMON_PIDS + + $ get-with-headers.py localhost:$HGPORT1 '?style=raw' + 200 Script output follows + + + /a/ + + +By default refreshing occurs every 20s and a new repo won't be listed +immediately. + + $ hg init $root/refreshtest/b + $ get-with-headers.py localhost:$HGPORT1 '?style=raw' + 200 Script output follows + + + /a/ + + +Restart the server with no refresh interval. New repo should appear +immediately. + + $ killdaemons.py + $ cat > paths.conf << EOF + > [web] + > refreshinterval = -1 + > [paths] + > / = $root/refreshtest/* + > EOF + $ hg serve -p $HGPORT1 -d --pid-file hg.pid --webdir-conf paths.conf + $ cat hg.pid >> $DAEMON_PIDS + + $ get-with-headers.py localhost:$HGPORT1 '?style=raw' + 200 Script output follows + + + /a/ + /b/ + + + $ hg init $root/refreshtest/c + $ get-with-headers.py localhost:$HGPORT1 '?style=raw' + 200 Script output follows + + + /a/ + /b/ + /c/ + + paths errors 1 $ cat error-paths-1.log diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-histedit-arguments.t --- a/tests/test-histedit-arguments.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-histedit-arguments.t Tue Sep 01 17:09:00 2015 -0500 @@ -69,7 +69,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -292,7 +292,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-histedit-bookmark-motion.t --- a/tests/test-histedit-bookmark-motion.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-histedit-bookmark-motion.t Tue Sep 01 17:09:00 2015 -0500 @@ -75,7 +75,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg histedit 1 --commands - --verbose << EOF | grep histedit @@ -136,7 +136,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg histedit 1 --commands - --verbose << EOF | grep histedit diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-histedit-commute.t --- a/tests/test-histedit-commute.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-histedit-commute.t Tue Sep 01 17:09:00 2015 -0500 @@ -69,7 +69,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved @@ -347,7 +347,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-histedit-obsolete.t --- a/tests/test-histedit-obsolete.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-histedit-obsolete.t Tue Sep 01 17:09:00 2015 -0500 @@ -54,7 +54,7 @@ # f, fold = use commit, but combine it with the one above # r, roll = like fold, but discard this commit's description # d, drop = remove commit from history - # m, mess = edit message without changing commit content + # m, mess = edit commit message without changing commit content # 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg histedit 1 --commands - --verbose < b1 + +log -r "follow('set:clean()')" + + $ hg log -r "follow('set:clean()')" + changeset: 0:67e992f2c4f3 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: base + + changeset: 1:3d5bf5654eda + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: r1 + + $ hg ci -Amb1 -d '1 0' adding b1 created new head @@ -646,7 +661,26 @@ summary: base +log -r follow('glob:b*') + $ hg log -r "follow('glob:b*')" + changeset: 0:67e992f2c4f3 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: base + + changeset: 1:3d5bf5654eda + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: r1 + + changeset: 3:e62f78d544b4 + tag: tip + parent: 1:3d5bf5654eda + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: b1 + log -f -r '1 + 4' $ hg up -C 0 @@ -673,6 +707,16 @@ date: Thu Jan 01 00:00:01 1970 +0000 summary: base +log -r "follow('set:grep(b2)')" + + $ hg log -r "follow('set:grep(b2)')" + changeset: 4:ddb82e70d1a1 + tag: tip + parent: 0:67e992f2c4f3 + user: test + date: Thu Jan 01 00:00:01 1970 +0000 + summary: b2 + log -f -r null $ hg log -f -r null diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-merge-internal-tools-pattern.t --- a/tests/test-merge-internal-tools-pattern.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-merge-internal-tools-pattern.t Tue Sep 01 17:09:00 2015 -0500 @@ -1,5 +1,6 @@ -Make sure that the internal merge tools (internal:fail, internal:local, and -internal:other) are used when matched by a merge-pattern in hgrc +Make sure that the internal merge tools (internal:fail, internal:local, +internal:union and internal:other) are used when matched by a +merge-pattern in hgrc Make sure HGMERGE doesn't interfere with the test: @@ -110,3 +111,31 @@ $ hg stat M f +Merge using internal:union tool: + + $ hg update -C 2 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ echo "line 4a" >>f + $ hg ci -Am "Adding fourth line (commit 4)" + $ hg update 2 + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + + $ echo "line 4b" >>f + $ hg ci -Am "Adding fourth line v2 (commit 5)" + created new head + + $ echo "[merge-patterns]" > .hg/hgrc + $ echo "* = internal:union" >> .hg/hgrc + + $ hg merge 3 + merging f + 1 files updated, 0 files merged, 0 files removed, 0 files unresolved + (branch merge, don't forget to commit) + + $ cat f + line 1 + line 2 + third line + line 4b + line 4a diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-module-imports.t --- a/tests/test-module-imports.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-module-imports.t Tue Sep 01 17:09:00 2015 -0500 @@ -112,22 +112,5 @@ these may expose other cycles. $ hg locate 'mercurial/**.py' 'hgext/**.py' | sed 's-\\-/-g' | python "$import_checker" - - mercurial/dispatch.py mixed imports - stdlib: commands - relative: error, extensions, fancyopts, hg, hook, util - mercurial/fileset.py mixed imports - stdlib: parser - relative: error, merge, util - mercurial/revset.py mixed imports - stdlib: parser - relative: error, hbisect, phases, util - mercurial/templater.py mixed imports - stdlib: parser - relative: config, error, templatefilters, templatekw, util - mercurial/ui.py mixed imports - stdlib: formatter - relative: config, error, progress, scmutil, util - Import cycle: mercurial.cmdutil -> mercurial.context -> mercurial.subrepo -> mercurial.cmdutil Import cycle: hgext.largefiles.basestore -> hgext.largefiles.localstore -> hgext.largefiles.basestore - Import cycle: mercurial.commands -> mercurial.commandserver -> mercurial.dispatch -> mercurial.commands [1] diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-mq.t --- a/tests/test-mq.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-mq.t Tue Sep 01 17:09:00 2015 -0500 @@ -39,7 +39,7 @@ By default, mq will automatically use git patches when required to avoid losing file mode changes, copy records, binary files or empty files creations - or deletions. This behaviour can be configured with: + or deletions. This behavior can be configured with: [mq] git = auto/keep/yes/no diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-parseindex.t --- a/tests/test-parseindex.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-parseindex.t Tue Sep 01 17:09:00 2015 -0500 @@ -60,9 +60,66 @@ $ cd .. -Test corrupted p1/p2 fields that could cause SEGV at parsers.c: +#if no-pure + +Test SEGV caused by bad revision passed to reachableroots() (issue4775): + + $ cd a -#if no-pure + $ python < from mercurial import changelog, scmutil + > cl = changelog.changelog(scmutil.vfs('.hg/store')) + > print 'good heads:' + > for head in [0, len(cl) - 1, -1]: + > print'%s: %r' % (head, cl.reachableroots(0, [head], [0])) + > print 'bad heads:' + > for head in [len(cl), 10000, -2, -10000, None]: + > print '%s:' % head, + > try: + > cl.reachableroots(0, [head], [0]) + > print 'uncaught buffer overflow?' + > except (IndexError, TypeError) as inst: + > print inst + > print 'good roots:' + > for root in [0, len(cl) - 1, -1]: + > print '%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])) + > print 'out-of-range roots are ignored:' + > for root in [len(cl), 10000, -2, -10000]: + > print '%s: %r' % (root, cl.reachableroots(root, [len(cl) - 1], [root])) + > print 'bad roots:' + > for root in [None]: + > print '%s:' % root, + > try: + > cl.reachableroots(root, [len(cl) - 1], [root]) + > print 'uncaught error?' + > except TypeError as inst: + > print inst + > EOF + good heads: + 0: [0] + 1: [0] + -1: [] + bad heads: + 2: head out of range + 10000: head out of range + -2: head out of range + -10000: head out of range + None: an integer is required + good roots: + 0: [0] + 1: [1] + -1: [-1] + out-of-range roots are ignored: + 2: [] + 10000: [] + -2: [] + -10000: [] + bad roots: + None: an integer is required + + $ cd .. + +Test corrupted p1/p2 fields that could cause SEGV at parsers.c: $ mkdir invalidparent $ cd invalidparent @@ -94,6 +151,8 @@ > cl = changelog.changelog(scmutil.vfs(sys.argv[1])) > n0, n1 = cl.node(0), cl.node(1) > ops = [ + > ('reachableroots', + > lambda: cl.index.reachableroots2(0, [1], [0], False)), > ('compute_phases_map_sets', lambda: cl.computephases([[0], []])), > ('index_headrevs', lambda: cl.headrevs()), > ('find_gca_candidates', lambda: cl.commonancestorsheads(n0, n1)), @@ -109,11 +168,13 @@ > EOF $ python test.py limit/.hg/store + reachableroots: parent out of range compute_phases_map_sets: parent out of range index_headrevs: parent out of range find_gca_candidates: parent out of range find_deepest: parent out of range $ python test.py segv/.hg/store + reachableroots: parent out of range compute_phases_map_sets: parent out of range index_headrevs: parent out of range find_gca_candidates: parent out of range diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-pathencode.py --- a/tests/test-pathencode.py Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-pathencode.py Tue Sep 01 17:09:00 2015 -0500 @@ -2,7 +2,7 @@ # time it is invoked, and tests the encoding of those pathnames. # # It uses a simple probabilistic model to generate valid pathnames -# that have proven likely to expose bugs and divergent behaviour in +# that have proven likely to expose bugs and divergent behavior in # different encoding implementations. from mercurial import store diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-revset.t --- a/tests/test-revset.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-revset.t Tue Sep 01 17:09:00 2015 -0500 @@ -141,7 +141,7 @@ ('symbol', '3') ('symbol', '6')) * set: - + 3 5 6 @@ -197,11 +197,53 @@ > 7 - $ try -- '-a-b-c-' # complains - hg: parse error at 7: not a prefix: end - [255] - $ log -a-b-c- # succeeds with fallback + +names that should be caught by fallback mechanism + + $ try -- '-a-b-c-' + ('symbol', '-a-b-c-') + * set: + + 4 + $ log -a-b-c- + 4 + $ try '+a+b+c+' + ('symbol', '+a+b+c+') + * set: + + 3 + $ try '+a+b+c+:' + (rangepost + ('symbol', '+a+b+c+')) + * set: + + 3 4 + 5 + 6 + 7 + 8 + 9 + $ try ':+a+b+c+' + (rangepre + ('symbol', '+a+b+c+')) + * set: + + 0 + 1 + 2 + 3 + $ try -- '-a-b-c-:+a+b+c+' + (range + ('symbol', '-a-b-c-') + ('symbol', '+a+b+c+')) + * set: + + 4 + 3 + $ log '-a-b-c-:+a+b+c+' + 4 + 3 $ try -- -a-b-c--a # complains (minus @@ -949,7 +991,7 @@ ('symbol', '4'))) * set: , + , > 5 3 @@ -972,7 +1014,7 @@ * set: , - > + > 0 1 2 @@ -1473,10 +1515,16 @@ (single rev) $ hg diff -r 'tip^' -r 'tip^' - $ hg diff -r 'tip^::tip^ or tip^' + $ hg diff -r 'tip^:tip^' (single rev that does not looks like a range) + $ hg diff -r 'tip^::tip^ or tip^' + diff -r d5d0dcbdc4d9 .hgtags + --- /dev/null Thu Jan 01 00:00:00 1970 +0000 + +++ b/.hgtags * (glob) + @@ -0,0 +1,1 @@ + +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0 $ hg diff -r 'tip^ or tip^' diff -r d5d0dcbdc4d9 .hgtags --- /dev/null Thu Jan 01 00:00:00 1970 +0000 diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-run-tests.t --- a/tests/test-run-tests.t Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-run-tests.t Tue Sep 01 17:09:00 2015 -0500 @@ -621,3 +621,17 @@ # Ran 1 tests, 0 skipped, 0 warned, 0 failed. #endif + +test support for --allow-slow-tests + $ cat > test-very-slow-test.t < #require slow + > $ echo pass + > pass + > EOF + $ run-tests.py test-very-slow-test.t + s + Skipped test-very-slow-test.t: skipped + # Ran 0 tests, 1 skipped, 0 warned, 0 failed. + $ run-tests.py --allow-slow-tests test-very-slow-test.t + . + # Ran 1 tests, 0 skipped, 0 warned, 0 failed. diff -r d0843f7e16b4 -r f77a3f27cea5 tests/test-wireproto.py --- a/tests/test-wireproto.py Tue Sep 01 16:38:33 2015 -0500 +++ b/tests/test-wireproto.py Tue Sep 01 17:09:00 2015 -0500 @@ -12,6 +12,10 @@ class clientpeer(wireproto.wirepeer): def __init__(self, serverrepo): self.serverrepo = serverrepo + + def _capabilities(self): + return ['batch'] + def _call(self, cmd, **args): return wireproto.dispatch(self.serverrepo, proto(args), cmd)