--- a/Makefile Tue Feb 25 18:45:01 2014 -0800
+++ b/Makefile Thu Feb 27 18:57:03 2014 -0600
@@ -102,7 +102,7 @@
update-pot: i18n/hg.pot
-i18n/hg.pot: $(PYFILES) $(DOCFILES)
+i18n/hg.pot: $(PYFILES) $(DOCFILES) i18n/posplit i18n/hggettext
$(PYTHON) i18n/hggettext mercurial/commands.py \
hgext/*.py hgext/*/__init__.py \
mercurial/fileset.py mercurial/revset.py \
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/Makefile.python Thu Feb 27 18:57:03 2014 -0600
@@ -0,0 +1,79 @@
+PYTHONVER=2.7.6
+PYTHONNAME=python-
+PREFIX=$(HOME)/bin/prefix-$(PYTHONNAME)$(PYTHONVER)
+SYMLINKDIR=$(HOME)/bin
+
+help:
+ @echo
+ @echo 'Make a custom installation of a Python version'
+ @echo
+ @echo 'Common make parameters:'
+ @echo ' PYTHONVER=... [$(PYTHONVER)]'
+ @echo ' PREFIX=... [$(PREFIX)]'
+ @echo ' SYMLINKDIR=... [$(SYMLINKDIR) creating $(PYTHONNAME)$(PYTHONVER)]'
+ @echo
+ @echo 'Common make targets:'
+ @echo ' python - install Python $$PYTHONVER in $$PREFIX'
+ @echo ' symlink - create a $$SYMLINKDIR/$(PYTHONNAME)$$PYTHONVER symlink'
+ @echo
+ @echo 'Example: create a temporary Python installation:'
+ @echo ' $$ make -f Makefile.python python PYTHONVER=2.4 PREFIX=/tmp/p24'
+ @echo ' $$ /tmp/p24/bin/python -V'
+ @echo ' Python 2.4'
+ @echo
+ @echo 'Some external libraries are required for building Python: zlib bzip2 openssl.'
+ @echo 'Make sure their development packages are installed systemwide.'
+# fedora: yum install zlib-devel bzip2-devel openssl-devel
+# debian: apt-get install zlib1g-dev libbz2-dev libssl-dev
+ @echo
+ @echo 'To build a nice collection of interesting Python versions:'
+ @echo ' $$ for v in 2.{4{,.2,.3},5{,.6},6{,.1,.2,.9},7{,.6}}; do'
+ @echo ' make -f Makefile.python symlink PYTHONVER=$$v || break; done'
+ @echo 'To run a Mercurial test on all these Python versions:'
+ @echo ' $$ for py in `cd ~/bin && ls $(PYTHONNAME)2.*`; do'
+ @echo ' echo $$py; $$py run-tests.py test-http.t; echo; done'
+ @echo
+
+export LANGUAGE=C
+export LC_ALL=C
+
+python: $(PREFIX)/bin/python docutils
+ printf 'import sys, zlib, bz2, docutils\nif sys.version_info >= (2,6):\n import ssl' | $(PREFIX)/bin/python
+
+PYTHON_SRCDIR=Python-$(PYTHONVER)
+PYTHON_SRCFILE=$(PYTHON_SRCDIR).tgz
+
+$(PREFIX)/bin/python:
+ [ -f $(PYTHON_SRCFILE) ] || wget http://www.python.org/ftp/python/$(PYTHONVER)/$(PYTHON_SRCFILE) || [ -f $(PYTHON_SRCFILE) ]
+ rm -rf $(PYTHON_SRCDIR)
+ tar xf $(PYTHON_SRCFILE)
+ # Ubuntu disables SSLv2 the hard way, disable it on old Pythons too
+ -sed -i 's,self.*SSLv2_method(),0;//\0,g' $(PYTHON_SRCDIR)/Modules/_ssl.c
+ # Find multiarch system libraries on Ubuntu with Python 2.4.x
+ # http://lipyrary.blogspot.dk/2011/05/how-to-compile-python-on-ubuntu-1104.html
+ -sed -i "s|lib_dirs = .* \[|\0'/usr/lib/`dpkg-architecture -qDEB_HOST_MULTIARCH`',|g" $(PYTHON_SRCDIR)/setup.py
+ # Find multiarch system libraries on Ubuntu and disable fortify error when setting argv
+ LDFLAGS="-L/usr/lib/`dpkg-architecture -qDEB_HOST_MULTIARCH`"; \
+ BASECFLAGS=-U_FORTIFY_SOURCE; \
+ export LDFLAGS BASECFLAGS; \
+ cd $(PYTHON_SRCDIR) && ./configure --prefix=$(PREFIX) && make all SVNVERSION=pwd && make install
+ printf 'import sys, zlib, bz2\nif sys.version_info >= (2,6):\n import ssl' | $(PREFIX)/bin/python
+ rm -rf $(PYTHON_SRCDIR)
+
+DOCUTILSVER=0.11
+DOCUTILS_SRCDIR=docutils-$(DOCUTILSVER)
+DOCUTILS_SRCFILE=$(DOCUTILS_SRCDIR).tar.gz
+
+docutils: $(PREFIX)/bin/python
+ @$(PREFIX)/bin/python -c 'import docutils' || ( set -ex; \
+ [ -f $(DOCUTILS_SRCFILE) ] || wget http://downloads.sourceforge.net/project/docutils/docutils/$(DOCUTILSVER)/$(DOCUTILS_SRCFILE) || [ -f $(DOCUTILS_SRCFILE) ]; \
+ rm -rf $(DOCUTILS_SRCDIR); \
+ tar xf $(DOCUTILS_SRCFILE); \
+ cd $(DOCUTILS_SRCDIR) && $(PREFIX)/bin/python setup.py install --prefix=$(PREFIX); \
+ $(PREFIX)/bin/python -c 'import docutils'; \
+ rm -rf $(DOCUTILS_SRCDIR); )
+
+symlink: python $(SYMLINKDIR)
+ ln -sf $(PREFIX)/bin/python $(SYMLINKDIR)/$(PYTHONNAME)$(PYTHONVER)
+
+.PHONY: help python docutils symlink
--- a/contrib/check-code.py Tue Feb 25 18:45:01 2014 -0800
+++ b/contrib/check-code.py Thu Feb 27 18:57:03 2014 -0600
@@ -150,6 +150,9 @@
"explicit exit code checks unnecessary"),
(uprefix + r'set -e', "don't use set -e"),
(uprefix + r'(\s|fi\b|done\b)', "use > for continued lines"),
+ (uprefix + r'.*:\.\S*/', "x:.y in a path does not work on msys, rewrite "
+ "as x://.y, or see `hg log -k msys` for alternatives", r'-\S+:\.|' #-Rxxx
+ 'hg pull -q file:../test'), # in test-pull.t which is skipped on windows
(r'^ saved backup bundle to \$TESTTMP.*\.hg$', winglobmsg),
(r'^ changeset .* references (corrupted|missing) \$TESTTMP/.*[^)]$',
winglobmsg),
@@ -162,6 +165,8 @@
(r'^ moving \S+/.*[^)]$', winglobmsg),
(r'^ no changes made to subrepo since.*/.*[^)]$', winglobmsg),
(r'^ .*: largefile \S+ not available from file:.*/.*[^)]$', winglobmsg),
+ (r'^ .*file://\$TESTTMP',
+ 'write "file:/*/$TESTTMP" + (glob) to match on windows too'),
],
# warnings
[
@@ -306,6 +311,7 @@
txtpats = [
[
('\s$', 'trailing whitespace'),
+ ('.. note::[ \n][^\n]', 'add two newlines after note::')
],
[]
]
--- a/contrib/hgfixes/fix_bytesmod.py Tue Feb 25 18:45:01 2014 -0800
+++ b/contrib/hgfixes/fix_bytesmod.py Thu Feb 27 18:57:03 2014 -0600
@@ -33,9 +33,10 @@
'''
def transform(self, node, results):
- if self.filename in blacklist:
- return
- elif self.filename == 'mercurial/util.py':
+ for bfn in blacklist:
+ if self.filename.endswith(bfn):
+ return
+ if not self.filename.endswith('mercurial/py3kcompat.py'):
touch_import('.', 'py3kcompat', node=node)
formatstr = results['formatstr'].clone()
@@ -60,4 +61,3 @@
call = Call(Name('bytesformatter', prefix=' '), args)
return call
-
--- a/contrib/import-checker.py Tue Feb 25 18:45:01 2014 -0800
+++ b/contrib/import-checker.py Thu Feb 27 18:57:03 2014 -0600
@@ -11,12 +11,15 @@
def dotted_name_of_path(path):
"""Given a relative path to a source file, return its dotted module name.
-
>>> dotted_name_of_path('mercurial/error.py')
'mercurial.error'
+ >>> dotted_name_of_path('zlibmodule.so')
+ 'zlib'
"""
parts = path.split('/')
- parts[-1] = parts[-1][:-3] # remove .py
+ parts[-1] = parts[-1].split('.', 1)[0] # remove .py and .so and .ARCH.so
+ if parts[-1].endswith('module'):
+ parts[-1] = parts[-1][:-6]
return '.'.join(parts)
@@ -136,7 +139,7 @@
http://bugs.python.org/issue19510.
>>> list(verify_stdlib_on_own_line('import sys, foo'))
- ['mixed stdlib and relative imports:\\n foo, sys']
+ ['mixed imports\\n stdlib: sys\\n relative: foo']
>>> list(verify_stdlib_on_own_line('import sys, os'))
[]
>>> list(verify_stdlib_on_own_line('import foo, bar'))
@@ -144,13 +147,13 @@
"""
for node in ast.walk(ast.parse(source)):
if isinstance(node, ast.Import):
- from_stdlib = {}
+ from_stdlib = {False: [], True: []}
for n in node.names:
- from_stdlib[n.name] = n.name in stdlib_modules
- num_std = len([x for x in from_stdlib.values() if x])
- if num_std not in (len(from_stdlib.values()), 0):
- yield ('mixed stdlib and relative imports:\n %s' %
- ', '.join(sorted(from_stdlib.iterkeys())))
+ from_stdlib[n.name in stdlib_modules].append(n.name)
+ if from_stdlib[True] and from_stdlib[False]:
+ yield ('mixed imports\n stdlib: %s\n relative: %s' %
+ (', '.join(sorted(from_stdlib[True])),
+ ', '.join(sorted(from_stdlib[False]))))
class CircularImport(Exception):
pass
--- a/hgext/color.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/color.py Thu Feb 27 18:57:03 2014 -0600
@@ -426,6 +426,16 @@
_("when to colorize (boolean, always, auto, or never)"),
_('TYPE')))
+def debugcolor(ui, repo, **opts):
+ global _styles
+ _styles = {}
+ for effect in _effects.keys():
+ _styles[effect] = effect
+ ui.write(('colormode: %s\n') % ui._colormode)
+ ui.write(_('available colors:\n'))
+ for label, colors in _styles.items():
+ ui.write(('%s\n') % colors, label=label)
+
if os.name != 'nt':
w32effects = None
else:
@@ -555,3 +565,8 @@
finally:
# Explicitly reset original attributes
_kernel32.SetConsoleTextAttribute(stdout, origattr)
+
+cmdtable = {
+ 'debugcolor':
+ (debugcolor, [], ('hg debugcolor'))
+}
--- a/hgext/convert/__init__.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/convert/__init__.py Thu Feb 27 18:57:03 2014 -0600
@@ -141,6 +141,14 @@
branch names. This can be used to (for instance) move code in one
repository from "default" to a named branch.
+ The closemap is a file that allows closing of a branch. This is useful if
+ you want to close a branch. Each entry contains a revision or hash
+ separated by white space.
+
+ The tagpmap is a file that exactly analogous to the branchmap. This will
+ rename tags on the fly and prevent the 'update tags' commit usually found
+ at the end of a convert process.
+
Mercurial Source
################
@@ -319,6 +327,10 @@
_('splice synthesized history into place'), _('FILE')),
('', 'branchmap', '',
_('change branch names while converting'), _('FILE')),
+ ('', 'closemap', '',
+ _('closes given revs'), _('FILE')),
+ ('', 'tagmap', '',
+ _('change tag names while converting'), _('FILE')),
('', 'branchsort', None, _('try to sort changesets by branches')),
('', 'datesort', None, _('try to sort changesets by date')),
('', 'sourcesort', None, _('preserve source changesets order')),
--- a/hgext/convert/common.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/convert/common.py Thu Feb 27 18:57:03 2014 -0600
@@ -63,13 +63,13 @@
self.encoding = 'utf-8'
- def checkhexformat(self, revstr):
+ def checkhexformat(self, revstr, mapname='splicemap'):
""" fails if revstr is not a 40 byte hex. mercurial and git both uses
such format for their revision numbering
"""
if not re.match(r'[0-9a-fA-F]{40,40}$', revstr):
- raise util.Abort(_('splicemap entry %s is not a valid revision'
- ' identifier') % revstr)
+ raise util.Abort(_('%s entry %s is not a valid revision'
+ ' identifier') % (mapname, revstr))
def before(self):
pass
@@ -172,7 +172,7 @@
"""
return {}
- def checkrevformat(self, revstr):
+ def checkrevformat(self, revstr, mapname='splicemap'):
"""revstr is a string that describes a revision in the given
source control system. Return true if revstr has correct
format.
@@ -192,10 +192,6 @@
self.path = path
self.created = []
- def getheads(self):
- """Return a list of this repository's heads"""
- raise NotImplementedError
-
def revmapfile(self):
"""Path to a file that will contain lines
source_rev_id sink_rev_id
@@ -208,7 +204,8 @@
mapping equivalent authors identifiers for each system."""
return None
- def putcommit(self, files, copies, parents, commit, source, revmap):
+ def putcommit(self, files, copies, parents, commit, source,
+ revmap, tagmap):
"""Create a revision with all changed files listed in 'files'
and having listed parents. 'commit' is a commit object
containing at a minimum the author, date, and message for this
--- a/hgext/convert/convcmd.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/convert/convcmd.py Thu Feb 27 18:57:03 2014 -0600
@@ -120,6 +120,43 @@
self.splicemap = self.parsesplicemap(opts.get('splicemap'))
self.branchmap = mapfile(ui, opts.get('branchmap'))
+ self.closemap = self.parseclosemap(opts.get('closemap'))
+ self.tagmap = mapfile(ui, opts.get('tagmap'))
+
+ def parseclosemap(self, path):
+ """ check and validate the closemap format and
+ return a list of revs to close.
+ Format checking has two parts.
+ 1. generic format which is same across all source types
+ 2. specific format checking which may be different for
+ different source type. This logic is implemented in
+ checkrevformat function in source files like
+ hg.py, subversion.py etc.
+ """
+
+ if not path:
+ return []
+ m = []
+ try:
+ fp = open(path, 'r')
+ for i, line in enumerate(fp):
+ line = line.splitlines()[0].rstrip()
+ if not line:
+ # Ignore blank lines
+ continue
+ # split line
+ lex = shlex.shlex(line, posix=True)
+ lex.whitespace_split = True
+ lex.whitespace += ','
+ line = list(lex)
+ for part in line:
+ self.source.checkrevformat(part, 'closemap')
+ m.extend(line)
+ # if file does not exist or error reading, exit
+ except IOError:
+ raise util.Abort(_('closemap file not found or error reading %s:')
+ % path)
+ return m
def parsesplicemap(self, path):
""" check and validate the splicemap format and
@@ -408,8 +445,11 @@
except KeyError:
parents = [b[0] for b in pbranches]
source = progresssource(self.ui, self.source, len(files))
+ if self.closemap and rev in self.closemap:
+ commit.extra['close'] = 1
+
newnode = self.dest.putcommit(files, copies, parents, commit,
- source, self.map)
+ source, self.map, self.tagmap)
source.close()
self.source.converted(rev, newnode)
self.map[rev] = newnode
@@ -445,6 +485,9 @@
self.ui.progress(_('converting'), None)
tags = self.source.gettags()
+ tags = dict((self.tagmap.get(k, k), v)
+ for k, v in tags.iteritems())
+
ctags = {}
for k in tags:
v = tags[k]
--- a/hgext/convert/git.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/convert/git.py Thu Feb 27 18:57:03 2014 -0600
@@ -297,7 +297,7 @@
return bookmarks
- def checkrevformat(self, revstr):
+ def checkrevformat(self, revstr, mapname='splicemap'):
""" git revision string is a 40 byte hex """
- self.checkhexformat(revstr)
+ self.checkhexformat(revstr, mapname)
--- a/hgext/convert/hg.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/convert/hg.py Thu Feb 27 18:57:03 2014 -0600
@@ -25,6 +25,9 @@
from common import NoRepo, commit, converter_source, converter_sink
+import re
+sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
+
class mercurial_sink(converter_sink):
def __init__(self, ui, path):
converter_sink.__init__(self, ui, path)
@@ -75,10 +78,6 @@
def authorfile(self):
return self.repo.join("authormap")
- def getheads(self):
- h = self.repo.changelog.heads()
- return [hex(x) for x in h]
-
def setbranch(self, branch, pbranches):
if not self.clonebranches:
return
@@ -117,7 +116,7 @@
self.repo.pull(prepo, [prepo.lookup(h) for h in heads])
self.before()
- def _rewritetags(self, source, revmap, data):
+ def _rewritetags(self, source, revmap, tagmap, data):
fp = cStringIO.StringIO()
for line in data.splitlines():
s = line.split(' ', 1)
@@ -126,17 +125,18 @@
revid = revmap.get(source.lookuprev(s[0]))
if not revid:
continue
- fp.write('%s %s\n' % (revid, s[1]))
+ fp.write('%s %s\n' % (revid, tagmap.get(s[1], s[1])))
return fp.getvalue()
- def putcommit(self, files, copies, parents, commit, source, revmap):
+ def putcommit(self, files, copies, parents, commit, source,
+ revmap, tagmap):
files = dict(files)
def getfilectx(repo, memctx, f):
v = files[f]
data, mode = source.getfile(f, v)
if f == '.hgtags':
- data = self._rewritetags(source, revmap, data)
+ data = self._rewritetags(source, revmap, tagmap, data)
return context.memfilectx(f, data, 'l' in mode, 'x' in mode,
copies.get(f))
@@ -157,6 +157,14 @@
p2 = parents.pop(0)
text = commit.desc
+
+ sha1s = re.findall(sha1re, text)
+ for sha1 in sha1s:
+ oldrev = source.lookuprev(sha1)
+ newrev = revmap.get(oldrev)
+ if newrev is not None:
+ text = text.replace(sha1, newrev[:len(sha1)])
+
extra = commit.extra.copy()
if self.branchnames and commit.branch:
extra['branch'] = commit.branch
@@ -190,14 +198,36 @@
parentctx = None
tagparent = nullid
- try:
- oldlines = sorted(parentctx['.hgtags'].data().splitlines(True))
- except Exception:
- oldlines = []
+ oldlines = set()
+ for branch, heads in self.repo.branchmap().iteritems():
+ for h in heads:
+ if '.hgtags' in self.repo[h]:
+ oldlines.update(
+ set(self.repo[h]['.hgtags'].data().splitlines(True)))
+ oldlines = sorted(list(oldlines))
newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
if newlines == oldlines:
return None, None
+
+ # if the old and new tags match, then there is nothing to update
+ oldtags = set()
+ newtags = set()
+ for line in oldlines:
+ s = line.strip().split(' ', 1)
+ if len(s) != 2:
+ continue
+ oldtags.add(s[1])
+ for line in newlines:
+ s = line.strip().split(' ', 1)
+ if len(s) != 2:
+ continue
+ if s[1] not in oldtags:
+ newtags.add(s[1].strip())
+
+ if not newtags:
+ return None, None
+
data = "".join(newlines)
def getfilectx(repo, memctx, f):
return context.memfilectx(f, data, False, False, None)
@@ -412,6 +442,6 @@
def getbookmarks(self):
return bookmarks.listbookmarks(self.repo)
- def checkrevformat(self, revstr):
+ def checkrevformat(self, revstr, mapname='splicemap'):
""" Mercurial, revision string is a 40 byte hex """
- self.checkhexformat(revstr)
+ self.checkhexformat(revstr, mapname)
--- a/hgext/convert/subversion.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/convert/subversion.py Thu Feb 27 18:57:03 2014 -0600
@@ -41,13 +41,30 @@
pass
def revsplit(rev):
- """Parse a revision string and return (uuid, path, revnum)."""
- url, revnum = rev.rsplit('@', 1)
- parts = url.split('/', 1)
+ """Parse a revision string and return (uuid, path, revnum).
+ >>> revsplit('svn:a2147622-4a9f-4db4-a8d3-13562ff547b2'
+ ... '/proj%20B/mytrunk/mytrunk@1')
+ ('a2147622-4a9f-4db4-a8d3-13562ff547b2', '/proj%20B/mytrunk/mytrunk', 1)
+ >>> revsplit('svn:8af66a51-67f5-4354-b62c-98d67cc7be1d@1')
+ ('', '', 1)
+ >>> revsplit('@7')
+ ('', '', 7)
+ >>> revsplit('7')
+ ('', '', 0)
+ >>> revsplit('bad')
+ ('', '', 0)
+ """
+ parts = rev.rsplit('@', 1)
+ revnum = 0
+ if len(parts) > 1:
+ revnum = int(parts[1])
+ parts = parts[0].split('/', 1)
+ uuid = ''
mod = ''
- if len(parts) > 1:
+ if len(parts) > 1 and parts[0].startswith('svn:'):
+ uuid = parts[0][4:]
mod = '/' + parts[1]
- return parts[0][4:], mod, int(revnum)
+ return uuid, mod, revnum
def quote(s):
# As of svn 1.7, many svn calls expect "canonical" paths. In
@@ -157,6 +174,30 @@
self._stdout.close()
self._stdout = None
+class directlogstream(list):
+ """Direct revision log iterator.
+ This can be used for debugging and development but it will probably leak
+ memory and is not suitable for real conversions."""
+ def __init__(self, url, paths, start, end, limit=0,
+ discover_changed_paths=True, strict_node_history=False):
+
+ def receiver(orig_paths, revnum, author, date, message, pool):
+ paths = {}
+ if orig_paths is not None:
+ for k, v in orig_paths.iteritems():
+ paths[k] = changedpath(v)
+ self.append((paths, revnum, author, date, message))
+
+ # Use an ra of our own so that our parent can consume
+ # our results without confusing the server.
+ t = transport.SvnRaTransport(url=url)
+ svn.ra.get_log(t.ra, paths, start, end, limit,
+ discover_changed_paths,
+ strict_node_history,
+ receiver)
+
+ def close(self):
+ pass
# Check to see if the given path is a local Subversion repo. Verify this by
# looking for several svn-specific files and directories in the given
@@ -454,13 +495,13 @@
del self.commits[rev]
return commit
- def checkrevformat(self, revstr):
+ def checkrevformat(self, revstr, mapname='splicemap'):
""" fails if revision format does not match the correct format"""
if not re.match(r'svn:[0-9a-f]{8,8}-[0-9a-f]{4,4}-'
'[0-9a-f]{4,4}-[0-9a-f]{4,4}-[0-9a-f]'
'{12,12}(.*)\@[0-9]+$',revstr):
- raise util.Abort(_('splicemap entry %s is not a valid revision'
- ' identifier') % revstr)
+ raise util.Abort(_('%s entry %s is not a valid revision'
+ ' identifier') % (mapname, revstr))
def gettags(self):
tags = {}
@@ -975,6 +1016,9 @@
relpaths.append(p.strip('/'))
args = [self.baseurl, relpaths, start, end, limit,
discover_changed_paths, strict_node_history]
+ # undocumented feature: debugsvnlog can be disabled
+ if not self.ui.configbool('convert', 'svn.debugsvnlog', True):
+ return directlogstream(*args)
arg = encodeargs(args)
hgexe = util.hgexecutable()
cmd = '%s debugsvnlog' % util.shellquote(hgexe)
@@ -1183,7 +1227,8 @@
def revid(self, rev):
return u"svn:%s@%s" % (self.uuid, rev)
- def putcommit(self, files, copies, parents, commit, source, revmap):
+ def putcommit(self, files, copies, parents, commit, source,
+ revmap, tagmap):
for parent in parents:
try:
return self.revid(self.childmap[parent])
--- a/hgext/histedit.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/histedit.py Thu Feb 27 18:57:03 2014 -0600
@@ -30,10 +30,12 @@
# Edit history between c561b4e977df and 7c2fd3b9020c
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
@@ -49,10 +51,12 @@
# Edit history between c561b4e977df and 7c2fd3b9020c
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
@@ -170,10 +174,12 @@
# i18n: command names and abbreviations must remain untranslated
editcomment = _("""# Edit history between %s and %s
#
+# Commits are listed from least to most recent
+#
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
-# f, fold = use commit, but fold into previous commit (combines N and N-1)
+# f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
--- a/hgext/largefiles/overrides.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/largefiles/overrides.py Thu Feb 27 18:57:03 2014 -0600
@@ -12,7 +12,7 @@
import copy
from mercurial import hg, commands, util, cmdutil, scmutil, match as match_, \
- node, archival, error, merge, discovery, pathutil
+ node, archival, error, merge, discovery, pathutil, revset
from mercurial.i18n import _
from mercurial.node import hex
from hgext import rebase
@@ -752,7 +752,7 @@
firstpulled = repo.firstpulled
except AttributeError:
raise util.Abort(_("pulled() only available in --lfrev"))
- return [r for r in subset if r >= firstpulled]
+ return revset.baseset([r for r in subset if r >= firstpulled])
def overrideclone(orig, ui, source, dest=None, **opts):
d = dest
--- a/hgext/mq.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/mq.py Thu Feb 27 18:57:03 2014 -0600
@@ -1986,9 +1986,11 @@
raise util.Abort(_('-e is incompatible with import from -'))
filename = normname(filename)
self.checkreservedname(filename)
- originpath = self.join(filename)
- if not os.path.isfile(originpath):
- raise util.Abort(_("patch %s does not exist") % filename)
+ if util.url(filename).islocal():
+ originpath = self.join(filename)
+ if not os.path.isfile(originpath):
+ raise util.Abort(
+ _("patch %s does not exist") % filename)
if patchname:
self.checkpatchname(patchname, force)
@@ -3407,7 +3409,7 @@
"""
revset.getargs(x, 0, 0, _("mq takes no arguments"))
applied = set([repo[r.node].rev() for r in repo.mq.applied])
- return [r for r in subset if r in applied]
+ return revset.baseset([r for r in subset if r in applied])
# tell hggettext to extract docstrings from these functions:
i18nfunctions = [revsetmq]
--- a/hgext/rebase.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/rebase.py Thu Feb 27 18:57:03 2014 -0600
@@ -703,7 +703,8 @@
if new != nullrev and new in seen:
skipped.add(old)
seen.add(new)
- repo.ui.debug('computed skipped revs: %s\n' % skipped)
+ repo.ui.debug('computed skipped revs: %s\n' %
+ (' '.join(str(r) for r in sorted(skipped)) or None))
repo.ui.debug('rebase status resumed\n')
return (originalwd, target, state, skipped,
collapse, keep, keepbranches, external, activebookmark)
@@ -790,7 +791,7 @@
repo.ui.debug('source is a child of destination\n')
return None
- repo.ui.debug('rebase onto %d starting from %s\n' % (dest, roots))
+ repo.ui.debug('rebase onto %d starting from %s\n' % (dest, root))
state.update(dict.fromkeys(rebaseset, nullrev))
# Rebase tries to turn <dest> into a parent of <root> while
# preserving the number of parents of rebased changesets:
--- a/hgext/shelve.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/shelve.py Thu Feb 27 18:57:03 2014 -0600
@@ -22,8 +22,8 @@
"""
from mercurial.i18n import _
-from mercurial.node import nullid, bin, hex
-from mercurial import changegroup, cmdutil, scmutil, phases
+from mercurial.node import nullid, nullrev, bin, hex
+from mercurial import changegroup, cmdutil, scmutil, phases, commands
from mercurial import error, hg, mdiff, merge, patch, repair, util
from mercurial import templatefilters
from mercurial import lock as lockmod
@@ -122,22 +122,21 @@
"""subcommand that creates a new shelve"""
def publicancestors(ctx):
- """Compute the heads of the public ancestors of a commit.
+ """Compute the public ancestors of a commit.
- Much faster than the revset heads(ancestors(ctx) - draft())"""
- seen = set()
+ Much faster than the revset ancestors(ctx) & draft()"""
+ seen = set([nullrev])
visit = util.deque()
visit.append(ctx)
while visit:
ctx = visit.popleft()
+ yield ctx.node()
for parent in ctx.parents():
rev = parent.rev()
if rev not in seen:
seen.add(rev)
if parent.mutable():
visit.append(parent)
- else:
- yield parent.node()
wctx = repo[None]
parents = wctx.parents()
@@ -173,9 +172,9 @@
repo.mq.checkapplied = saved
if parent.node() != nullid:
- desc = parent.description().split('\n', 1)[0]
+ desc = "changes to '%s'" % parent.description().split('\n', 1)[0]
else:
- desc = '(empty repository)'
+ desc = '(changes in empty repository)'
if not opts['message']:
opts['message'] = desc
@@ -518,6 +517,7 @@
if not shelvedfile(repo, basename, 'files').exists():
raise util.Abort(_("shelved change '%s' not found") % basename)
+ oldquiet = ui.quiet
wlock = lock = tr = None
try:
lock = repo.lock()
@@ -537,6 +537,8 @@
# Store pending changes in a commit
m, a, r, d = repo.status()[:4]
if m or a or r or d:
+ ui.status(_("temporarily committing pending changes "
+ "(restore with 'hg unshelve --abort')\n"))
def commitfunc(ui, repo, message, match, opts):
hasmq = util.safehasattr(repo, 'mq')
if hasmq:
@@ -551,15 +553,12 @@
tempopts = {}
tempopts['message'] = "pending changes temporary commit"
- oldquiet = ui.quiet
- try:
- ui.quiet = True
- node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
- finally:
- ui.quiet = oldquiet
+ ui.quiet = True
+ node = cmdutil.commit(ui, repo, commitfunc, [], tempopts)
tmpwctx = repo[node]
try:
+ ui.quiet = True
fp = shelvedfile(repo, basename, 'hg').opener()
gen = changegroup.readbundle(fp, fp.name)
repo.addchangegroup(gen, 'unshelve', 'bundle:' + fp.name)
@@ -568,11 +567,14 @@
finally:
fp.close()
+ ui.quiet = oldquiet
+
shelvectx = repo['tip']
# If the shelve is not immediately on top of the commit
# we'll be merging with, rebase it to be on top.
if tmpwctx.node() != shelvectx.parents()[0].node():
+ ui.status(_('rebasing shelved changes\n'))
try:
rebase.rebase(ui, repo, **{
'rev' : [shelvectx.rev()],
@@ -610,6 +612,7 @@
unshelvecleanup(ui, repo, basename, opts)
finally:
+ ui.quiet = oldquiet
if tr:
tr.release()
lockmod.release(lock, wlock)
@@ -632,8 +635,8 @@
('p', 'patch', None,
_('show patch')),
('', 'stat', None,
- _('output diffstat-style summary of changes'))],
- _('hg shelve'))
+ _('output diffstat-style summary of changes'))] + commands.walkopts,
+ _('hg shelve [OPTION]... [FILE]...'))
def shelvecmd(ui, repo, *pats, **opts):
'''save and set aside changes from the working directory
--- a/hgext/transplant.py Tue Feb 25 18:45:01 2014 -0800
+++ b/hgext/transplant.py Thu Feb 27 18:57:03 2014 -0600
@@ -670,7 +670,8 @@
s = revset.getset(repo, subset, x)
else:
s = subset
- return [r for r in s if repo[r].extra().get('transplant_source')]
+ return revset.baseset([r for r in s if
+ repo[r].extra().get('transplant_source')])
def kwtransplanted(repo, ctx, **args):
""":transplanted: String. The node identifier of the transplanted
--- a/i18n/check-translation.py Tue Feb 25 18:45:01 2014 -0800
+++ b/i18n/check-translation.py Thu Feb 27 18:57:03 2014 -0600
@@ -66,6 +66,46 @@
def warningchecker(msgidpat=None):
return checker('warning', msgidpat)
+@warningchecker()
+def taildoublecolons(pe):
+ """Check equality of tail '::'-ness between msgid and msgstr
+
+ >>> pe = polib.POEntry(
+ ... msgid ='ends with ::',
+ ... msgstr='ends with ::')
+ >>> for e in taildoublecolons(pe): print e
+ >>> pe = polib.POEntry(
+ ... msgid ='ends with ::',
+ ... msgstr='ends without double-colons')
+ >>> for e in taildoublecolons(pe): print e
+ tail '::'-ness differs between msgid and msgstr
+ >>> pe = polib.POEntry(
+ ... msgid ='ends without double-colons',
+ ... msgstr='ends with ::')
+ >>> for e in taildoublecolons(pe): print e
+ tail '::'-ness differs between msgid and msgstr
+ """
+ if pe.msgid.endswith('::') != pe.msgstr.endswith('::'):
+ yield "tail '::'-ness differs between msgid and msgstr"
+
+@warningchecker()
+def indentation(pe):
+ """Check equality of initial indentation between msgid and msgstr
+
+ This may report unexpected warning, because this doesn't aware
+ the syntax of rst document and the context of msgstr.
+
+ >>> pe = polib.POEntry(
+ ... msgid =' indented text',
+ ... msgstr=' narrowed indentation')
+ >>> for e in indentation(pe): print e
+ initial indentation width differs betweeen msgid and msgstr
+ """
+ idindent = len(pe.msgid) - len(pe.msgid.lstrip())
+ strindent = len(pe.msgstr) - len(pe.msgstr.lstrip())
+ if idindent != strindent:
+ yield "initial indentation width differs betweeen msgid and msgstr"
+
####################
def check(pofile, fatal=True, warning=False):
--- a/i18n/de.po Tue Feb 25 18:45:01 2014 -0800
+++ b/i18n/de.po Thu Feb 27 18:57:03 2014 -0600
@@ -20,7 +20,7 @@
msgstr ""
"Project-Id-Version: Mercurial\n"
"Report-Msgid-Bugs-To: <mercurial-devel@selenic.com>\n"
-"POT-Creation-Date: 2014-01-25 17:51+0100\n"
+"POT-Creation-Date: 2014-01-29 16:47+0100\n"
"PO-Revision-Date: 2013-09-30 20:52+0100\n"
"Last-Translator: Simon Heimberg <simohe@besonet.ch>\n"
"Language-Team: \n"
@@ -2928,6 +2928,7 @@
" [repository]\n"
" native = LF"
+#. do not translate: .. note::
msgid ".. note::"
msgstr ""
@@ -5029,6 +5030,7 @@
" Siehe Hilfe zu 'paths' zu Pfad-Kurznamen und 'urls' für erlaubte\n"
" Formate für die Quellangabe."
+#. do not translate: .. container::
msgid " .. container:: verbose"
msgstr ""
@@ -6548,6 +6550,7 @@
" Ohne Argumente werden die aktuell aktiven Wächter ausgegeben.\n"
" Mit einem Argument wird der aktuelle Wächter gesetzt."
+#. do not translate: .. note::
msgid " .. note::"
msgstr ""
@@ -15694,6 +15697,7 @@
" order until one or more configuration files are detected."
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note:: The registry key ``HKEY_LOCAL_MACHINE\\SOFTWARE\\Wow6432Node"
"\\Mercurial``\n"
@@ -15873,6 +15877,7 @@
msgid " stable5 = latest -b stable"
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note:: It is possible to create aliases with the same names as\n"
" existing commands, which will then override the original\n"
@@ -15918,6 +15923,7 @@
"echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``."
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note:: Some global configuration options such as ``-R`` are\n"
" processed before shell aliases and will thus not be passed to\n"
@@ -16101,6 +16107,7 @@
"the command."
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note:: The tempfile mechanism is recommended for Windows systems,\n"
" where the standard shell I/O redirection operators often have\n"
@@ -16572,6 +16579,7 @@
" update failed (e.g. because conflicts not resolved), ``$HG_ERROR=1``."
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note:: It is generally better to use standard hooks rather than the\n"
" generic pre- and post- command hooks as they are guaranteed to be\n"
@@ -16580,6 +16588,7 @@
" generate a commit (e.g. tag) and not just the commit command."
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note:: Environment variables with empty values may not be passed to\n"
" hooks on platforms such as Windows. As an example, ``$HG_PARENT2``\n"
@@ -18967,6 +18976,7 @@
":Manual group: Mercurial Manual"
msgstr ""
+#. do not translate: .. contents::
msgid ""
".. contents::\n"
" :backlinks: top\n"
@@ -19017,6 +19027,7 @@
" repository."
msgstr ""
+#. do not translate: .. include::
msgid ".. include:: hg.1.gendoc.txt"
msgstr ""
@@ -19121,6 +19132,7 @@
"Public License version 2 or any later version."
msgstr ""
+#. do not translate: .. include::
msgid ".. include:: common.txt\n"
msgstr ""
@@ -19143,6 +19155,7 @@
":Manual group: Mercurial Manual"
msgstr ""
+#. do not translate: .. include::
msgid ".. include:: hgignore.5.gendoc.txt"
msgstr ""
@@ -19170,6 +19183,7 @@
"Public License version 2 or any later version."
msgstr ""
+#. do not translate: .. include::
msgid ".. include:: common.txt"
msgstr ""
@@ -19281,6 +19295,7 @@
"regexp pattern, start it with ``^``."
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note::\n"
" Patterns specified in other than ``.hgignore`` are always rooted.\n"
@@ -19333,6 +19348,7 @@
":Manual group: Mercurial Manual"
msgstr ""
+#. do not translate: .. contents::
msgid ""
".. contents::\n"
" :backlinks: top\n"
@@ -19348,6 +19364,7 @@
"Beschreibung\n"
"============"
+#. do not translate: .. include::
msgid ".. include:: hgrc.5.gendoc.txt"
msgstr ""
@@ -19564,6 +19581,7 @@
msgid "8. The merge of the file fails and must be resolved before commit."
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note::\n"
" After selecting a merge program, Mercurial will by default attempt\n"
@@ -19633,6 +19651,7 @@
msgid "Alternate pattern notations must be specified explicitly."
msgstr "Andere Schreibweisen von Mustern müssen explizit angegeben werden."
+#. do not translate: .. note::
msgid ""
".. note::\n"
" Patterns specified in ``.hgignore`` are not rooted.\n"
@@ -19804,6 +19823,7 @@
msgid " - secret changesets are neither pushed, pulled, or cloned"
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note::\n"
" Pulling a draft changeset from a publishing server does not mark it\n"
@@ -19823,12 +19843,14 @@
" [phases]\n"
" publish = False"
+#. do not translate: .. note::
msgid ""
".. note::\n"
" Servers running older versions of Mercurial are treated as\n"
" publishing."
msgstr ""
+#. do not translate: .. note::
msgid ""
".. note::\n"
" Changesets in secret phase are not exchanged with the server. This\n"
@@ -20216,6 +20238,7 @@
" repositories states when committing in the parent repository."
msgstr ""
+#. do not translate: .. note::
msgid ""
" .. note::\n"
" The ``.hgsubstate`` file should not be edited manually."
--- a/i18n/posplit Tue Feb 25 18:45:01 2014 -0800
+++ b/i18n/posplit Thu Feb 27 18:57:03 2014 -0600
@@ -5,6 +5,7 @@
# license: MIT/X11/Expat
#
+import re
import sys
import polib
@@ -30,6 +31,7 @@
cache = {}
entries = po[:]
po[:] = []
+ findd = re.compile(r' *\.\. (\w+)::') # for finding directives
for entry in entries:
msgids = entry.msgid.split(u'\n\n')
if entry.msgstr:
@@ -49,8 +51,27 @@
delta = 0
for msgid, msgstr in zip(msgids, msgstrs):
- if msgid:
+ if msgid and msgid != '::':
newentry = mkentry(entry, delta, msgid, msgstr)
+ mdirective = findd.match(msgid)
+ if mdirective:
+ if not msgid[mdirective.end():].rstrip():
+ # only directive, nothing to translate here
+ continue
+ directive = mdirective.group(1)
+ if directive in ('container', 'include'):
+ if msgid.rstrip('\n').count('\n') == 0:
+ # only rst syntax, nothing to translate
+ continue
+ else:
+ # lines following directly, unexpected
+ print 'Warning: text follows line with directive' \
+ ' %s' % directive
+ comment = 'do not translate: .. %s::' % directive
+ if not newentry.comment:
+ newentry.comment = comment
+ elif comment not in newentry.comment:
+ newentry.comment += '\n' + comment
addentry(po, newentry, cache)
delta += 2 + msgid.count('\n')
po.save()
--- a/mercurial/ancestor.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/ancestor.py Thu Feb 27 18:57:03 2014 -0600
@@ -31,7 +31,7 @@
poison = 1 << (i + 1)
gca = set()
- interesting = left = len(nodes)
+ interesting = len(nodes)
nv = len(seen) - 1
while nv >= 0 and interesting:
v = nv
@@ -45,10 +45,8 @@
gca.add(v)
sv |= poison
if v in nodes:
- left -= 1
- if left <= 1:
- # history is linear
- return set([v])
+ # history is linear
+ return set([v])
if sv < poison:
for p in pfunc(v):
sp = seen[p]
--- a/mercurial/bookmarks.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/bookmarks.py Thu Feb 27 18:57:03 2014 -0600
@@ -363,22 +363,6 @@
writer(msg)
localmarks.write()
-def updateremote(ui, repo, remote, revs):
- ui.debug("checking for updated bookmarks\n")
- revnums = map(repo.changelog.rev, revs or [])
- ancestors = [a for a in repo.changelog.ancestors(revnums, inclusive=True)]
- (addsrc, adddst, advsrc, advdst, diverge, differ, invalid
- ) = compare(repo, repo._bookmarks, remote.listkeys('bookmarks'),
- srchex=hex)
-
- for b, scid, dcid in advsrc:
- if ancestors and repo[scid].rev() not in ancestors:
- continue
- if remote.pushkey('bookmarks', b, dcid, scid):
- ui.status(_("updating bookmark %s\n") % b)
- else:
- ui.warn(_('updating bookmark %s failed!\n') % b)
-
def pushtoremote(ui, repo, remote, targets):
(addsrc, adddst, advsrc, advdst, diverge, differ, invalid
) = compare(repo, repo._bookmarks, remote.listkeys('bookmarks'),
--- a/mercurial/cmdutil.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/cmdutil.py Thu Feb 27 18:57:03 2014 -0600
@@ -10,7 +10,7 @@
import os, sys, errno, re, tempfile
import util, scmutil, templater, patch, error, templatekw, revlog, copies
import match as matchmod
-import subrepo, context, repair, graphmod, revset, phases, obsolete, pathutil
+import context, repair, graphmod, revset, phases, obsolete, pathutil
import changelog
import bookmarks
import lock as lockmod
@@ -542,6 +542,131 @@
if runfn:
return runfn()
+def tryimportone(ui, repo, hunk, parents, opts, msgs, updatefunc):
+ """Utility function used by commands.import to import a single patch
+
+ This function is explicitly defined here to help the evolve extension to
+ wrap this part of the import logic.
+
+ The API is currently a bit ugly because it a simple code translation from
+ the import command. Feel free to make it better.
+
+ :hunk: a patch (as a binary string)
+ :parents: nodes that will be parent of the created commit
+ :opts: the full dict of option passed to the import command
+ :msgs: list to save commit message to.
+ (used in case we need to save it when failing)
+ :updatefunc: a function that update a repo to a given node
+ updatefunc(<repo>, <node>)
+ """
+ tmpname, message, user, date, branch, nodeid, p1, p2 = \
+ patch.extract(ui, hunk)
+
+ editor = commiteditor
+ if opts.get('edit'):
+ editor = commitforceeditor
+ update = not opts.get('bypass')
+ strip = opts["strip"]
+ sim = float(opts.get('similarity') or 0)
+ if not tmpname:
+ return (None, None)
+ msg = _('applied to working directory')
+
+ try:
+ cmdline_message = logmessage(ui, opts)
+ if cmdline_message:
+ # pickup the cmdline msg
+ message = cmdline_message
+ elif message:
+ # pickup the patch msg
+ message = message.strip()
+ else:
+ # launch the editor
+ message = None
+ ui.debug('message:\n%s\n' % message)
+
+ if len(parents) == 1:
+ parents.append(repo[nullid])
+ if opts.get('exact'):
+ if not nodeid or not p1:
+ raise util.Abort(_('not a Mercurial patch'))
+ p1 = repo[p1]
+ p2 = repo[p2 or nullid]
+ elif p2:
+ try:
+ p1 = repo[p1]
+ p2 = repo[p2]
+ # Without any options, consider p2 only if the
+ # patch is being applied on top of the recorded
+ # first parent.
+ if p1 != parents[0]:
+ p1 = parents[0]
+ p2 = repo[nullid]
+ except error.RepoError:
+ p1, p2 = parents
+ else:
+ p1, p2 = parents
+
+ n = None
+ if update:
+ if p1 != parents[0]:
+ updatefunc(repo, p1.node())
+ if p2 != parents[1]:
+ repo.setparents(p1.node(), p2.node())
+
+ if opts.get('exact') or opts.get('import_branch'):
+ repo.dirstate.setbranch(branch or 'default')
+
+ files = set()
+ patch.patch(ui, repo, tmpname, strip=strip, files=files,
+ eolmode=None, similarity=sim / 100.0)
+ files = list(files)
+ if opts.get('no_commit'):
+ if message:
+ msgs.append(message)
+ else:
+ if opts.get('exact') or p2:
+ # If you got here, you either use --force and know what
+ # you are doing or used --exact or a merge patch while
+ # being updated to its first parent.
+ m = None
+ else:
+ m = scmutil.matchfiles(repo, files or [])
+ n = repo.commit(message, opts.get('user') or user,
+ opts.get('date') or date, match=m,
+ editor=editor)
+ else:
+ if opts.get('exact') or opts.get('import_branch'):
+ branch = branch or 'default'
+ else:
+ branch = p1.branch()
+ store = patch.filestore()
+ try:
+ files = set()
+ try:
+ patch.patchrepo(ui, repo, p1, store, tmpname, strip,
+ files, eolmode=None)
+ except patch.PatchError, e:
+ raise util.Abort(str(e))
+ memctx = context.makememctx(repo, (p1.node(), p2.node()),
+ message,
+ opts.get('user') or user,
+ opts.get('date') or date,
+ branch, files, store,
+ editor=commiteditor)
+ repo.savecommitmessage(memctx.description())
+ n = memctx.commit()
+ finally:
+ store.close()
+ if opts.get('exact') and hex(n) != nodeid:
+ raise util.Abort(_('patch is damaged or loses information'))
+ if n:
+ # i18n: refers to a short changeset id
+ msg = _('created %s') % short(n)
+ return (msg, n)
+ finally:
+ os.unlink(tmpname)
+
def export(repo, revs, template='hg-%h.patch', fp=None, switch_parent=False,
opts=None):
'''export changesets as hg patches.'''
@@ -629,7 +754,7 @@
if listsubrepos:
ctx1 = repo[node1]
ctx2 = repo[node2]
- for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
+ for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
tempnode2 = node2
try:
if node2 is not None:
@@ -974,6 +1099,19 @@
t.use_template(tmpl)
return t
+def showmarker(ui, marker):
+ """utility function to display obsolescence marker in a readable way
+
+ To be used by debug function."""
+ ui.write(hex(marker.precnode()))
+ for repl in marker.succnodes():
+ ui.write(' ')
+ ui.write(hex(repl))
+ ui.write(' %X ' % marker._data[2])
+ ui.write('{%s}' % (', '.join('%r: %r' % t for t in
+ sorted(marker.metadata().items()))))
+ ui.write('\n')
+
def finddate(ui, repo, date):
"""Find the tipmost changeset that matches the given date spec"""
@@ -995,19 +1133,11 @@
raise util.Abort(_("revision matching date not found"))
-def increasingwindows(start, end, windowsize=8, sizelimit=512):
- if start < end:
- while start < end:
- yield start, min(windowsize, end - start)
- start += windowsize
- if windowsize < sizelimit:
- windowsize *= 2
- else:
- while start > end:
- yield start, min(windowsize, start - end - 1)
- start -= windowsize
- if windowsize < sizelimit:
- windowsize *= 2
+def increasingwindows(windowsize=8, sizelimit=512):
+ while True:
+ yield windowsize
+ if windowsize < sizelimit:
+ windowsize *= 2
class FileWalkError(Exception):
pass
@@ -1132,7 +1262,7 @@
elif follow:
revs = repo.revs('reverse(:.)')
else:
- revs = list(repo)
+ revs = revset.baseset(repo)
revs.reverse()
if not revs:
return []
@@ -1148,7 +1278,7 @@
if not slowpath and not match.files():
# No files, no patterns. Display all revs.
- wanted = set(revs)
+ wanted = revs
if not slowpath and match.files():
# We only have to read through the filelog to find wanted revisions
@@ -1250,14 +1380,7 @@
stop = min(revs[0], revs[-1])
for x in xrange(rev, stop - 1, -1):
if ff.match(x):
- wanted.discard(x)
-
- # Choose a small initial window if we will probably only visit a
- # few commits.
- limit = loglimit(opts)
- windowsize = 8
- if limit:
- windowsize = min(limit, windowsize)
+ wanted = wanted - [x]
# Now that wanted is correctly initialized, we can iterate over the
# revision range, yielding only revisions in wanted.
@@ -1270,8 +1393,18 @@
def want(rev):
return rev in wanted
- for i, window in increasingwindows(0, len(revs), windowsize):
- nrevs = [rev for rev in revs[i:i + window] if want(rev)]
+ it = iter(revs)
+ stopiteration = False
+ for windowsize in increasingwindows():
+ nrevs = []
+ for i in xrange(windowsize):
+ try:
+ rev = it.next()
+ if want(rev):
+ nrevs.append(rev)
+ except (StopIteration):
+ stopiteration = True
+ break
for rev in sorted(nrevs):
fns = fncache.get(rev)
ctx = change(rev)
@@ -1284,6 +1417,10 @@
prepare(ctx, fns)
for rev in nrevs:
yield change(rev)
+
+ if stopiteration:
+ break
+
return iterate()
def _makegraphfilematcher(repo, pats, followfirst):
@@ -1471,10 +1608,11 @@
if follow and len(repo) > 0:
revs = repo.revs('reverse(:.)')
else:
- revs = list(repo.changelog)
+ revs = revset.baseset(repo.changelog)
revs.reverse()
if not revs:
return [], None, None
+ revs = revset.baseset(revs)
expr, filematcher = _makegraphlogrevset(repo, pats, opts, revs)
if possiblyunsorted:
revs.sort(reverse=True)
@@ -2057,54 +2195,8 @@
handle(revert, False)
else:
handle(remove, False)
-
if not opts.get('dry_run'):
- def checkout(f):
- fc = ctx[f]
- repo.wwrite(f, fc.data(), fc.flags())
-
- audit_path = pathutil.pathauditor(repo.root)
- for f in remove[0]:
- if repo.dirstate[f] == 'a':
- repo.dirstate.drop(f)
- continue
- audit_path(f)
- try:
- util.unlinkpath(repo.wjoin(f))
- except OSError:
- pass
- repo.dirstate.remove(f)
-
- normal = None
- if node == parent:
- # We're reverting to our parent. If possible, we'd like status
- # to report the file as clean. We have to use normallookup for
- # merges to avoid losing information about merged/dirty files.
- if p2 != nullid:
- normal = repo.dirstate.normallookup
- else:
- normal = repo.dirstate.normal
- for f in revert[0]:
- checkout(f)
- if normal:
- normal(f)
-
- for f in add[0]:
- checkout(f)
- repo.dirstate.add(f)
-
- normal = repo.dirstate.normallookup
- if node == parent and p2 == nullid:
- normal = repo.dirstate.normal
- for f in undelete[0]:
- checkout(f)
- normal(f)
-
- copied = copies.pathcopies(repo[parent], ctx)
-
- for f in add[0] + undelete[0] + revert[0]:
- if f in copied:
- repo.dirstate.copy(copied[f], f)
+ _performrevert(repo, parents, ctx, revert, add, remove, undelete)
if targetsubs:
# Revert the subrepos on the revert list
@@ -2113,6 +2205,63 @@
finally:
wlock.release()
+def _performrevert(repo, parents, ctx, revert, add, remove, undelete):
+ """function that actually perform all the action computed for revert
+
+ This is an independent function to let extension to plug in and react to
+ the imminent revert.
+
+ Make sure you have the working directory locked when caling this function.
+ """
+ parent, p2 = parents
+ node = ctx.node()
+ def checkout(f):
+ fc = ctx[f]
+ repo.wwrite(f, fc.data(), fc.flags())
+
+ audit_path = pathutil.pathauditor(repo.root)
+ for f in remove[0]:
+ if repo.dirstate[f] == 'a':
+ repo.dirstate.drop(f)
+ continue
+ audit_path(f)
+ try:
+ util.unlinkpath(repo.wjoin(f))
+ except OSError:
+ pass
+ repo.dirstate.remove(f)
+
+ normal = None
+ if node == parent:
+ # We're reverting to our parent. If possible, we'd like status
+ # to report the file as clean. We have to use normallookup for
+ # merges to avoid losing information about merged/dirty files.
+ if p2 != nullid:
+ normal = repo.dirstate.normallookup
+ else:
+ normal = repo.dirstate.normal
+ for f in revert[0]:
+ checkout(f)
+ if normal:
+ normal(f)
+
+ for f in add[0]:
+ checkout(f)
+ repo.dirstate.add(f)
+
+ normal = repo.dirstate.normallookup
+ if node == parent and p2 == nullid:
+ normal = repo.dirstate.normal
+ for f in undelete[0]:
+ checkout(f)
+ normal(f)
+
+ copied = copies.pathcopies(repo[parent], ctx)
+
+ for f in add[0] + undelete[0] + revert[0]:
+ if f in copied:
+ repo.dirstate.copy(copied[f], f)
+
def command(table):
'''returns a function object bound to table which can be used as
a decorator for populating table as a command table'''
--- a/mercurial/commands.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/commands.py Thu Feb 27 18:57:03 2014 -0600
@@ -1461,6 +1461,82 @@
cmdutil.commitstatus(repo, node, branch, bheads, opts)
+@command('config|showconfig|debugconfig',
+ [('u', 'untrusted', None, _('show untrusted configuration options')),
+ ('e', 'edit', None, _('start editor'))],
+ _('[-u] [NAME]...'))
+def config(ui, repo, *values, **opts):
+ """show combined config settings from all hgrc files
+
+ With no arguments, print names and values of all config items.
+
+ With one argument of the form section.name, print just the value
+ of that config item.
+
+ With multiple arguments, print names and values of all config
+ items with matching section names.
+
+ With --debug, the source (filename and line number) is printed
+ for each config item.
+
+ Returns 0 on success.
+ """
+
+ if opts.get('edit'):
+ paths = scmutil.userrcpath()
+ for f in paths:
+ if os.path.exists(f):
+ break
+ else:
+ f = paths[0]
+ fp = open(f, "w")
+ fp.write(
+ '# example config (see "hg help config" for more info)\n'
+ '\n'
+ '[ui]\n'
+ '# set this to your full email address\n'
+ 'username =\n'
+ '\n'
+ '[extensions]\n'
+ '# uncomment these lines to enable some popular extensions\n'
+ '# (see "hg help extensions" for more info)\n'
+ '# pager =\n'
+ '# progress =\n'
+ '# color =\n')
+ fp.close()
+
+ editor = ui.geteditor()
+ util.system("%s \"%s\"" % (editor, f),
+ onerr=util.Abort, errprefix=_("edit failed"),
+ out=ui.fout)
+ return
+
+ for f in scmutil.rcpath():
+ ui.debug('read config from: %s\n' % f)
+ untrusted = bool(opts.get('untrusted'))
+ if values:
+ sections = [v for v in values if '.' not in v]
+ items = [v for v in values if '.' in v]
+ if len(items) > 1 or items and sections:
+ raise util.Abort(_('only one config item permitted'))
+ for section, name, value in ui.walkconfig(untrusted=untrusted):
+ value = str(value).replace('\n', '\\n')
+ sectname = section + '.' + name
+ if values:
+ for v in values:
+ if v == section:
+ ui.debug('%s: ' %
+ ui.configsource(section, name, untrusted))
+ ui.write('%s=%s\n' % (sectname, value))
+ elif v == sectname:
+ ui.debug('%s: ' %
+ ui.configsource(section, name, untrusted))
+ ui.write(value, '\n')
+ else:
+ ui.debug('%s: ' %
+ ui.configsource(section, name, untrusted))
+ ui.write('%s=%s\n' % (sectname, value))
+
@command('copy|cp',
[('A', 'after', None, _('record a copy that has already occurred')),
('f', 'force', None, _('forcibly copy over an existing managed file')),
@@ -1943,7 +2019,7 @@
tree = fileset.parse(expr)[0]
ui.note(tree, "\n")
- for f in fileset.getfileset(ctx, expr):
+ for f in ctx.getfileset(expr):
ui.write("%s\n" % f)
@command('debugfsinfo', [], _('[PATH]'))
@@ -2107,10 +2183,21 @@
import templater
p = templater.templatepath()
ui.status(_("checking templates (%s)...\n") % ' '.join(p))
- try:
- templater.templater(templater.templatepath("map-cmdline.default"))
- except Exception, inst:
- ui.write(" %s\n" % inst)
+ if p:
+ m = templater.templatepath("map-cmdline.default")
+ if m:
+ # template found, check if it is working
+ try:
+ templater.templater(m)
+ except Exception, inst:
+ ui.write(" %s\n" % inst)
+ p = None
+ else:
+ ui.write(_(" template 'default' not found\n"))
+ p = None
+ else:
+ ui.write(_(" no template directories found\n"))
+ if not p:
ui.write(_(" (templates seem to have been installed incorrectly)\n"))
problems += 1
@@ -2216,14 +2303,7 @@
l.release()
else:
for m in obsolete.allmarkers(repo):
- ui.write(hex(m.precnode()))
- for repl in m.succnodes():
- ui.write(' ')
- ui.write(hex(repl))
- ui.write(' %X ' % m._data[2])
- ui.write('{%s}' % (', '.join('%r: %r' % t for t in
- sorted(m.metadata().items()))))
- ui.write('\n')
+ cmdutil.showmarker(ui, m)
@command('debugpathcomplete',
[('f', 'full', None, _('complete an entire path')),
@@ -2544,8 +2624,10 @@
ui.write(('deltas against other : ') + fmt % pcfmt(numother,
numdeltas))
-@command('debugrevspec', [], ('REVSPEC'))
-def debugrevspec(ui, repo, expr):
+@command('debugrevspec',
+ [('', 'optimize', None, _('print parsed tree after optimizing'))],
+ ('REVSPEC'))
+def debugrevspec(ui, repo, expr, **opts):
"""parse and apply a revision specification
Use --verbose to print the parsed tree before and after aliases
@@ -2557,8 +2639,11 @@
newtree = revset.findaliases(ui, tree)
if newtree != tree:
ui.note(revset.prettyformat(newtree), "\n")
+ if opts["optimize"]:
+ weight, optimizedtree = revset.optimize(newtree, True)
+ ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
func = revset.match(ui, expr)
- for c in func(repo, range(len(repo))):
+ for c in func(repo, revset.spanset(repo)):
ui.write("%s\n" % c)
@command('debugsetparents', [], _('REV1 [REV2]'))
@@ -3669,10 +3754,6 @@
if date:
opts['date'] = util.parsedate(date)
- editor = cmdutil.commiteditor
- if opts.get('edit'):
- editor = cmdutil.commitforceeditor
-
update = not opts.get('bypass')
if not update and opts.get('no_commit'):
raise util.Abort(_('cannot use --no-commit with --bypass'))
@@ -3691,112 +3772,9 @@
cmdutil.bailifchanged(repo)
base = opts["base"]
- strip = opts["strip"]
wlock = lock = tr = None
msgs = []
- def tryone(ui, hunk, parents):
- tmpname, message, user, date, branch, nodeid, p1, p2 = \
- patch.extract(ui, hunk)
-
- if not tmpname:
- return (None, None)
- msg = _('applied to working directory')
-
- try:
- cmdline_message = cmdutil.logmessage(ui, opts)
- if cmdline_message:
- # pickup the cmdline msg
- message = cmdline_message
- elif message:
- # pickup the patch msg
- message = message.strip()
- else:
- # launch the editor
- message = None
- ui.debug('message:\n%s\n' % message)
-
- if len(parents) == 1:
- parents.append(repo[nullid])
- if opts.get('exact'):
- if not nodeid or not p1:
- raise util.Abort(_('not a Mercurial patch'))
- p1 = repo[p1]
- p2 = repo[p2 or nullid]
- elif p2:
- try:
- p1 = repo[p1]
- p2 = repo[p2]
- # Without any options, consider p2 only if the
- # patch is being applied on top of the recorded
- # first parent.
- if p1 != parents[0]:
- p1 = parents[0]
- p2 = repo[nullid]
- except error.RepoError:
- p1, p2 = parents
- else:
- p1, p2 = parents
-
- n = None
- if update:
- if p1 != parents[0]:
- hg.clean(repo, p1.node())
- if p2 != parents[1]:
- repo.setparents(p1.node(), p2.node())
-
- if opts.get('exact') or opts.get('import_branch'):
- repo.dirstate.setbranch(branch or 'default')
-
- files = set()
- patch.patch(ui, repo, tmpname, strip=strip, files=files,
- eolmode=None, similarity=sim / 100.0)
- files = list(files)
- if opts.get('no_commit'):
- if message:
- msgs.append(message)
- else:
- if opts.get('exact') or p2:
- # If you got here, you either use --force and know what
- # you are doing or used --exact or a merge patch while
- # being updated to its first parent.
- m = None
- else:
- m = scmutil.matchfiles(repo, files or [])
- n = repo.commit(message, opts.get('user') or user,
- opts.get('date') or date, match=m,
- editor=editor)
- else:
- if opts.get('exact') or opts.get('import_branch'):
- branch = branch or 'default'
- else:
- branch = p1.branch()
- store = patch.filestore()
- try:
- files = set()
- try:
- patch.patchrepo(ui, repo, p1, store, tmpname, strip,
- files, eolmode=None)
- except patch.PatchError, e:
- raise util.Abort(str(e))
- memctx = context.makememctx(repo, (p1.node(), p2.node()),
- message,
- opts.get('user') or user,
- opts.get('date') or date,
- branch, files, store,
- editor=cmdutil.commiteditor)
- repo.savecommitmessage(memctx.description())
- n = memctx.commit()
- finally:
- store.close()
- if opts.get('exact') and hex(n) != nodeid:
- raise util.Abort(_('patch is damaged or loses information'))
- if n:
- # i18n: refers to a short changeset id
- msg = _('created %s') % short(n)
- return (msg, n)
- finally:
- os.unlink(tmpname)
try:
try:
@@ -3817,7 +3795,8 @@
haspatch = False
for hunk in patch.split(patchfile):
- (msg, node) = tryone(ui, hunk, parents)
+ (msg, node) = cmdutil.tryimportone(ui, repo, hunk, parents,
+ opts, msgs, hg.clean)
if msg:
haspatch = True
ui.note(msg + '\n')
@@ -4002,6 +3981,12 @@
each commit. When the -v/--verbose switch is used, the list of
changed files and full commit message are shown.
+ With --graph the revisions are shown as an ASCII art DAG with the most
+ recent changeset at the top.
+ 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
+ and '+' represents a fork where the changeset from the lines below is a
+ parent of the 'o' merge on the same same line.
+
.. note::
log -p/--patch may generate unexpected diff output for merge
@@ -4714,7 +4699,15 @@
dest, branches = hg.parseurl(dest, opts.get('branch'))
ui.status(_('pushing to %s\n') % util.hidepassword(dest))
revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
- other = hg.peer(repo, opts, dest)
+ 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
+
if revs:
revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
@@ -5247,52 +5240,6 @@
self.httpd.serve_forever()
-@command('showconfig|debugconfig',
- [('u', 'untrusted', None, _('show untrusted configuration options'))],
- _('[-u] [NAME]...'))
-def showconfig(ui, repo, *values, **opts):
- """show combined config settings from all hgrc files
-
- With no arguments, print names and values of all config items.
-
- With one argument of the form section.name, print just the value
- of that config item.
-
- With multiple arguments, print names and values of all config
- items with matching section names.
-
- With --debug, the source (filename and line number) is printed
- for each config item.
-
- Returns 0 on success.
- """
-
- for f in scmutil.rcpath():
- ui.debug('read config from: %s\n' % f)
- untrusted = bool(opts.get('untrusted'))
- if values:
- sections = [v for v in values if '.' not in v]
- items = [v for v in values if '.' in v]
- if len(items) > 1 or items and sections:
- raise util.Abort(_('only one config item permitted'))
- for section, name, value in ui.walkconfig(untrusted=untrusted):
- value = str(value).replace('\n', '\\n')
- sectname = section + '.' + name
- if values:
- for v in values:
- if v == section:
- ui.debug('%s: ' %
- ui.configsource(section, name, untrusted))
- ui.write('%s=%s\n' % (sectname, value))
- elif v == sectname:
- ui.debug('%s: ' %
- ui.configsource(section, name, untrusted))
- ui.write(value, '\n')
- else:
- ui.debug('%s: ' %
- ui.configsource(section, name, untrusted))
- ui.write('%s=%s\n' % (sectname, value))
-
@command('^status|st',
[('A', 'all', None, _('show status of all files')),
('m', 'modified', None, _('show only modified files')),
--- a/mercurial/context.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/context.py Thu Feb 27 18:57:03 2014 -0600
@@ -12,6 +12,7 @@
import os, errno, stat
import obsolete as obsmod
import repoview
+import fileset
propertycache = util.propertycache
@@ -79,6 +80,9 @@
def mutable(self):
return self.phase() > phases.public
+ def getfileset(self, expr):
+ return fileset.getfileset(self, expr)
+
def obsolete(self):
"""True if the changeset is obsolete"""
return self.rev() in obsmod.getrevs(self._repo, 'obsolete')
--- a/mercurial/discovery.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/discovery.py Thu Feb 27 18:57:03 2014 -0600
@@ -154,7 +154,7 @@
- branch: the branch name
- remoteheads: the list of remote heads known locally
- None is the branch is new
+ None if the branch is new
- newheads: the new remote heads (known locally) with outgoing pushed
- unsyncedheads: the list of remote heads unknown locally.
"""
@@ -250,8 +250,7 @@
hint=_("use 'hg push --new-branch' to create"
" new remote branches"))
- # 2 compute newly pushed bookmarks. We
- # we don't warned about bookmarked heads.
+ # 2. Compute newly pushed bookmarks. We don't warn about bookmarked heads.
localbookmarks = repo._bookmarks
remotebookmarks = remote.listkeys('bookmarks')
bookmarkedheads = set()
@@ -269,23 +268,23 @@
# If there are more heads after the push than before, a suitable
# error message, depending on unsynced status, is displayed.
error = None
- unsynced = False
allmissing = set(outgoing.missing)
allfuturecommon = set(c.node() for c in repo.set('%ld', outgoing.common))
allfuturecommon.update(allmissing)
for branch, heads in sorted(headssum.iteritems()):
- candidate_newhs = set(heads[1])
+ remoteheads, newheads, unsyncedheads = heads
+ candidate_newhs = set(newheads)
# add unsynced data
- if heads[0] is None:
+ if remoteheads is None:
oldhs = set()
else:
- oldhs = set(heads[0])
- oldhs.update(heads[2])
- candidate_newhs.update(heads[2])
- dhs = None
+ oldhs = set(remoteheads)
+ oldhs.update(unsyncedheads)
+ candidate_newhs.update(unsyncedheads)
+ dhs = None # delta heads, the new heads on branch
discardedheads = set()
if repo.obsstore:
- # remove future heads which are actually obsolete by another
+ # remove future heads which are actually obsoleted by another
# pushed element:
#
# XXX as above, There are several cases this case does not handle
@@ -297,8 +296,8 @@
# (2) if the new heads have ancestors which are not obsolete and
# not ancestors of any other heads we will have a new head too.
#
- # This two case will be easy to handle for know changeset but much
- # more tricky for unsynced changes.
+ # These two cases will be easy to handle for known changeset but
+ # much more tricky for unsynced changes.
newhs = set()
for nh in candidate_newhs:
if nh in repo and repo[nh].phase() <= phases.public:
@@ -312,10 +311,17 @@
newhs.add(nh)
else:
newhs = candidate_newhs
- if [h for h in heads[2] if h not in discardedheads]:
- unsynced = True
- if heads[0] is None:
- if 1 < len(newhs):
+ unsynced = sorted(h for h in unsyncedheads if h not in discardedheads)
+ if unsynced:
+ heads = ' '.join(short(h) for h in unsynced)
+ if branch is None:
+ repo.ui.status(_("remote has heads that are "
+ "not known locally: %s\n") % heads)
+ else:
+ repo.ui.status(_("remote has heads on branch '%s' that are "
+ "not known locally: %s\n") % (branch, heads))
+ if remoteheads is None:
+ if len(newhs) > 1:
dhs = list(newhs)
if error is None:
error = (_("push creates new branch '%s' "
@@ -324,7 +330,7 @@
" see \"hg help push\" for details about"
" pushing new heads")
elif len(newhs) > len(oldhs):
- # strip updates to existing remote heads from the new heads list
+ # remove bookmarked or existing remote heads from the new heads list
dhs = sorted(newhs - bookmarkedheads - oldhs)
if dhs:
if error is None:
@@ -334,7 +340,7 @@
else:
error = _("push creates new remote head %s!"
) % short(dhs[0])
- if heads[2]: # unsynced
+ if unsyncedheads:
hint = _("pull and merge or"
" see \"hg help push\" for details about"
" pushing new heads")
@@ -350,7 +356,3 @@
repo.ui.note((" %s\n") % short(h))
if error:
raise util.Abort(error, hint=hint)
-
- # 6. Check for unsynced changes on involved branches.
- if unsynced:
- repo.ui.warn(_("note: unsynced remote changes!\n"))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/exchange.py Thu Feb 27 18:57:03 2014 -0600
@@ -0,0 +1,530 @@
+# exchange.py - utily to exchange data between repo.
+#
+# Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+from i18n import _
+from node import hex, nullid
+import errno
+import util, scmutil, changegroup, base85
+import discovery, phases, obsolete, bookmarks
+
+
+class pushoperation(object):
+ """A object that represent a single push operation
+
+ It purpose is to carry push related state and very common operation.
+
+ A new should be created at the begining of each push and discarded
+ afterward.
+ """
+
+ def __init__(self, repo, remote, force=False, revs=None, newbranch=False):
+ # repo we push from
+ self.repo = repo
+ self.ui = repo.ui
+ # repo we push to
+ self.remote = remote
+ # force option provided
+ self.force = force
+ # revs to be pushed (None is "all")
+ self.revs = revs
+ # allow push of new branch
+ self.newbranch = newbranch
+ # did a local lock get acquired?
+ self.locallocked = None
+ # Integer version of the push result
+ # - None means nothing to push
+ # - 0 means HTTP error
+ # - 1 means we pushed and remote head count is unchanged *or*
+ # we have outgoing changesets but refused to push
+ # - other values as described by addchangegroup()
+ self.ret = None
+ # discover.outgoing object (contains common and outgoin data)
+ self.outgoing = None
+ # all remote heads before the push
+ self.remoteheads = None
+ # testable as a boolean indicating if any nodes are missing locally.
+ self.incoming = None
+ # set of all heads common after changeset bundle push
+ self.commonheads = None
+
+def push(repo, remote, force=False, revs=None, newbranch=False):
+ '''Push outgoing changesets (limited by revs) from a local
+ repository to remote. Return an integer:
+ - None means nothing to push
+ - 0 means HTTP error
+ - 1 means we pushed and remote head count is unchanged *or*
+ we have outgoing changesets but refused to push
+ - other values as described by addchangegroup()
+ '''
+ pushop = pushoperation(repo, remote, force, revs, newbranch)
+ if pushop.remote.local():
+ missing = (set(pushop.repo.requirements)
+ - pushop.remote.local().supported)
+ if missing:
+ msg = _("required features are not"
+ " supported in the destination:"
+ " %s") % (', '.join(sorted(missing)))
+ raise util.Abort(msg)
+
+ # there are two ways to push to remote repo:
+ #
+ # addchangegroup assumes local user can lock remote
+ # repo (local filesystem, old ssh servers).
+ #
+ # unbundle assumes local user cannot lock remote repo (new ssh
+ # servers, http servers).
+
+ if not pushop.remote.canpush():
+ raise util.Abort(_("destination does not support push"))
+ # get local lock as we might write phase data
+ locallock = None
+ try:
+ locallock = pushop.repo.lock()
+ pushop.locallocked = True
+ except IOError, err:
+ pushop.locallocked = False
+ if err.errno != errno.EACCES:
+ raise
+ # source repo cannot be locked.
+ # We do not abort the push, but just disable the local phase
+ # synchronisation.
+ msg = 'cannot lock source repository: %s\n' % err
+ pushop.ui.debug(msg)
+ try:
+ pushop.repo.checkpush(pushop.force, pushop.revs)
+ lock = None
+ unbundle = pushop.remote.capable('unbundle')
+ if not unbundle:
+ lock = pushop.remote.lock()
+ try:
+ _pushdiscovery(pushop)
+ if _pushcheckoutgoing(pushop):
+ _pushchangeset(pushop)
+ _pushcomputecommonheads(pushop)
+ _pushsyncphase(pushop)
+ _pushobsolete(pushop)
+ finally:
+ if lock is not None:
+ lock.release()
+ finally:
+ if locallock is not None:
+ locallock.release()
+
+ _pushbookmark(pushop)
+ return pushop.ret
+
+def _pushdiscovery(pushop):
+ # discovery
+ unfi = pushop.repo.unfiltered()
+ fci = discovery.findcommonincoming
+ commoninc = fci(unfi, pushop.remote, force=pushop.force)
+ common, inc, remoteheads = commoninc
+ fco = discovery.findcommonoutgoing
+ outgoing = fco(unfi, pushop.remote, onlyheads=pushop.revs,
+ commoninc=commoninc, force=pushop.force)
+ pushop.outgoing = outgoing
+ pushop.remoteheads = remoteheads
+ pushop.incoming = inc
+
+def _pushcheckoutgoing(pushop):
+ outgoing = pushop.outgoing
+ unfi = pushop.repo.unfiltered()
+ if not outgoing.missing:
+ # nothing to push
+ scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
+ return False
+ # something to push
+ if not pushop.force:
+ # if repo.obsstore == False --> no obsolete
+ # then, save the iteration
+ if unfi.obsstore:
+ # this message are here for 80 char limit reason
+ mso = _("push includes obsolete changeset: %s!")
+ mst = "push includes %s changeset: %s!"
+ # plain versions for i18n tool to detect them
+ _("push includes unstable changeset: %s!")
+ _("push includes bumped changeset: %s!")
+ _("push includes divergent changeset: %s!")
+ # If we are to push if there is at least one
+ # obsolete or unstable changeset in missing, at
+ # least one of the missinghead will be obsolete or
+ # unstable. So checking heads only is ok
+ for node in outgoing.missingheads:
+ ctx = unfi[node]
+ if ctx.obsolete():
+ raise util.Abort(mso % ctx)
+ elif ctx.troubled():
+ raise util.Abort(_(mst)
+ % (ctx.troubles()[0],
+ ctx))
+ newbm = pushop.ui.configlist('bookmarks', 'pushing')
+ discovery.checkheads(unfi, pushop.remote, outgoing,
+ pushop.remoteheads,
+ pushop.newbranch,
+ bool(pushop.incoming),
+ newbm)
+ return True
+
+def _pushchangeset(pushop):
+ """Make the actual push of changeset bundle to remote repo"""
+ outgoing = pushop.outgoing
+ unbundle = pushop.remote.capable('unbundle')
+ # TODO: get bundlecaps from remote
+ bundlecaps = None
+ # create a changegroup from local
+ if pushop.revs is None and not (outgoing.excluded
+ or pushop.repo.changelog.filteredrevs):
+ # push everything,
+ # use the fast path, no race possible on push
+ bundler = changegroup.bundle10(pushop.repo, bundlecaps)
+ cg = pushop.repo._changegroupsubset(outgoing,
+ bundler,
+ 'push',
+ fastpath=True)
+ else:
+ cg = pushop.repo.getlocalbundle('push', outgoing, bundlecaps)
+
+ # apply changegroup to remote
+ if unbundle:
+ # local repo finds heads on server, finds out what
+ # revs it must push. once revs transferred, if server
+ # finds it has different heads (someone else won
+ # commit/push race), server aborts.
+ if pushop.force:
+ remoteheads = ['force']
+ else:
+ remoteheads = pushop.remoteheads
+ # ssh: return remote's addchangegroup()
+ # http: return remote's addchangegroup() or 0 for error
+ pushop.ret = pushop.remote.unbundle(cg, remoteheads,
+ 'push')
+ else:
+ # we return an integer indicating remote head count
+ # change
+ pushop.ret = pushop.remote.addchangegroup(cg, 'push',
+ pushop.repo.url())
+
+def _pushcomputecommonheads(pushop):
+ unfi = pushop.repo.unfiltered()
+ if pushop.ret:
+ # push succeed, synchronize target of the push
+ cheads = pushop.outgoing.missingheads
+ elif pushop.revs is None:
+ # All out push fails. synchronize all common
+ cheads = pushop.outgoing.commonheads
+ else:
+ # I want cheads = heads(::missingheads and ::commonheads)
+ # (missingheads is revs with secret changeset filtered out)
+ #
+ # This can be expressed as:
+ # cheads = ( (missingheads and ::commonheads)
+ # + (commonheads and ::missingheads))"
+ # )
+ #
+ # while trying to push we already computed the following:
+ # common = (::commonheads)
+ # missing = ((commonheads::missingheads) - commonheads)
+ #
+ # We can pick:
+ # * missingheads part of common (::commonheads)
+ common = set(pushop.outgoing.common)
+ nm = pushop.repo.changelog.nodemap
+ cheads = [node for node in pushop.revs if nm[node] in common]
+ # and
+ # * commonheads parents on missing
+ revset = unfi.set('%ln and parents(roots(%ln))',
+ pushop.outgoing.commonheads,
+ pushop.outgoing.missing)
+ cheads.extend(c.node() for c in revset)
+ pushop.commonheads = cheads
+
+def _pushsyncphase(pushop):
+ """synchronise phase information locally and remotly"""
+ unfi = pushop.repo.unfiltered()
+ cheads = pushop.commonheads
+ if pushop.ret:
+ # push succeed, synchronize target of the push
+ cheads = pushop.outgoing.missingheads
+ elif pushop.revs is None:
+ # All out push fails. synchronize all common
+ cheads = pushop.outgoing.commonheads
+ else:
+ # I want cheads = heads(::missingheads and ::commonheads)
+ # (missingheads is revs with secret changeset filtered out)
+ #
+ # This can be expressed as:
+ # cheads = ( (missingheads and ::commonheads)
+ # + (commonheads and ::missingheads))"
+ # )
+ #
+ # while trying to push we already computed the following:
+ # common = (::commonheads)
+ # missing = ((commonheads::missingheads) - commonheads)
+ #
+ # We can pick:
+ # * missingheads part of common (::commonheads)
+ common = set(pushop.outgoing.common)
+ nm = pushop.repo.changelog.nodemap
+ cheads = [node for node in pushop.revs if nm[node] in common]
+ # and
+ # * commonheads parents on missing
+ revset = unfi.set('%ln and parents(roots(%ln))',
+ pushop.outgoing.commonheads,
+ pushop.outgoing.missing)
+ cheads.extend(c.node() for c in revset)
+ pushop.commonheads = cheads
+ # even when we don't push, exchanging phase data is useful
+ remotephases = pushop.remote.listkeys('phases')
+ if (pushop.ui.configbool('ui', '_usedassubrepo', False)
+ and remotephases # server supports phases
+ and pushop.ret is None # nothing was pushed
+ and remotephases.get('publishing', False)):
+ # When:
+ # - this is a subrepo push
+ # - and remote support phase
+ # - and no changeset was pushed
+ # - and remote is publishing
+ # We may be in issue 3871 case!
+ # We drop the possible phase synchronisation done by
+ # courtesy to publish changesets possibly locally draft
+ # on the remote.
+ remotephases = {'publishing': 'True'}
+ if not remotephases: # old server or public only rer
+ _localphasemove(pushop, cheads)
+ # don't push any phase data as there is nothing to push
+ else:
+ ana = phases.analyzeremotephases(pushop.repo, cheads,
+ remotephases)
+ pheads, droots = ana
+ ### Apply remote phase on local
+ if remotephases.get('publishing', False):
+ _localphasemove(pushop, cheads)
+ else: # publish = False
+ _localphasemove(pushop, pheads)
+ _localphasemove(pushop, cheads, phases.draft)
+ ### Apply local phase on remote
+
+ # Get the list of all revs draft on remote by public here.
+ # XXX Beware that revset break if droots is not strictly
+ # XXX root we may want to ensure it is but it is costly
+ outdated = unfi.set('heads((%ln::%ln) and public())',
+ droots, cheads)
+ for newremotehead in outdated:
+ r = pushop.remote.pushkey('phases',
+ newremotehead.hex(),
+ str(phases.draft),
+ str(phases.public))
+ if not r:
+ pushop.ui.warn(_('updating %s to public failed!\n')
+ % newremotehead)
+
+def _localphasemove(pushop, nodes, phase=phases.public):
+ """move <nodes> to <phase> in the local source repo"""
+ if pushop.locallocked:
+ phases.advanceboundary(pushop.repo, phase, nodes)
+ else:
+ # repo is not locked, do not change any phases!
+ # Informs the user that phases should have been moved when
+ # applicable.
+ actualmoves = [n for n in nodes if phase < pushop.repo[n].phase()]
+ phasestr = phases.phasenames[phase]
+ if actualmoves:
+ pushop.ui.status(_('cannot lock source repo, skipping '
+ 'local %s phase update\n') % phasestr)
+
+def _pushobsolete(pushop):
+ """utility function to push obsolete markers to a remote"""
+ pushop.ui.debug('try to push obsolete markers to remote\n')
+ repo = pushop.repo
+ remote = pushop.remote
+ if (obsolete._enabled and repo.obsstore and
+ 'obsolete' in remote.listkeys('namespaces')):
+ rslts = []
+ remotedata = repo.listkeys('obsolete')
+ for key in sorted(remotedata, reverse=True):
+ # reverse sort to ensure we end with dump0
+ data = remotedata[key]
+ rslts.append(remote.pushkey('obsolete', key, '', data))
+ if [r for r in rslts if not r]:
+ msg = _('failed to push some obsolete markers!\n')
+ repo.ui.warn(msg)
+
+def _pushbookmark(pushop):
+ """Update bookmark position on remote"""
+ ui = pushop.ui
+ repo = pushop.repo.unfiltered()
+ remote = pushop.remote
+ ui.debug("checking for updated bookmarks\n")
+ revnums = map(repo.changelog.rev, pushop.revs or [])
+ ancestors = [a for a in repo.changelog.ancestors(revnums, inclusive=True)]
+ (addsrc, adddst, advsrc, advdst, diverge, differ, invalid
+ ) = bookmarks.compare(repo, repo._bookmarks, remote.listkeys('bookmarks'),
+ srchex=hex)
+
+ for b, scid, dcid in advsrc:
+ if ancestors and repo[scid].rev() not in ancestors:
+ continue
+ if remote.pushkey('bookmarks', b, dcid, scid):
+ ui.status(_("updating bookmark %s\n") % b)
+ else:
+ ui.warn(_('updating bookmark %s failed!\n') % b)
+
+class pulloperation(object):
+ """A object that represent a single pull operation
+
+ It purpose is to carry push related state and very common operation.
+
+ A new should be created at the begining of each push and discarded
+ afterward.
+ """
+
+ def __init__(self, repo, remote, heads=None, force=False):
+ # repo we pull from
+ self.repo = repo
+ # repo we pull to
+ self.remote = remote
+ # revision we try to pull (None is "all")
+ self.heads = heads
+ # do we force pull?
+ self.force = force
+ # the name the pull transaction
+ self._trname = 'pull\n' + util.hidepassword(remote.url())
+ # hold the transaction once created
+ self._tr = None
+ # set of common changeset between local and remote before pull
+ self.common = None
+ # set of pulled head
+ self.rheads = None
+ # list of missing changeset to fetch remotly
+ self.fetch = None
+
+ @util.propertycache
+ def pulledsubset(self):
+ """heads of the set of changeset target by the pull"""
+ # compute target subset
+ if self.heads is None:
+ # We pulled every thing possible
+ # sync on everything common
+ return self.common + self.rheads
+ else:
+ # We pulled a specific subset
+ # sync on this subset
+ return self.heads
+
+ def gettransaction(self):
+ """get appropriate pull transaction, creating it if needed"""
+ if self._tr is None:
+ self._tr = self.repo.transaction(self._trname)
+ return self._tr
+
+ def closetransaction(self):
+ """close transaction if created"""
+ if self._tr is not None:
+ self._tr.close()
+
+ def releasetransaction(self):
+ """release transaction if created"""
+ if self._tr is not None:
+ self._tr.release()
+
+def pull(repo, remote, heads=None, force=False):
+ pullop = pulloperation(repo, remote, heads, force)
+ if pullop.remote.local():
+ missing = set(pullop.remote.requirements) - pullop.repo.supported
+ if missing:
+ msg = _("required features are not"
+ " supported in the destination:"
+ " %s") % (', '.join(sorted(missing)))
+ raise util.Abort(msg)
+
+ lock = pullop.repo.lock()
+ try:
+ tmp = discovery.findcommonincoming(pullop.repo.unfiltered(),
+ pullop.remote,
+ heads=pullop.heads,
+ force=force)
+ pullop.common, pullop.fetch, pullop.rheads = tmp
+ if not pullop.fetch:
+ pullop.repo.ui.status(_("no changes found\n"))
+ result = 0
+ else:
+ result = _pullchangeset(pullop)
+
+ _pullphase(pullop)
+ _pullobsolete(pullop)
+ pullop.closetransaction()
+ finally:
+ pullop.releasetransaction()
+ lock.release()
+
+ return result
+
+def _pullchangeset(pullop):
+ """pull changeset from unbundle into the local repo"""
+ # We delay the open of the transaction as late as possible so we
+ # don't open transaction for nothing or you break future useful
+ # rollback call
+ pullop.gettransaction()
+ if pullop.heads is None and list(pullop.common) == [nullid]:
+ pullop.repo.ui.status(_("requesting all changes\n"))
+ elif pullop.heads is None and pullop.remote.capable('changegroupsubset'):
+ # issue1320, avoid a race if remote changed after discovery
+ pullop.heads = pullop.rheads
+
+ if pullop.remote.capable('getbundle'):
+ # TODO: get bundlecaps from remote
+ cg = pullop.remote.getbundle('pull', common=pullop.common,
+ heads=pullop.heads or pullop.rheads)
+ elif pullop.heads is None:
+ cg = pullop.remote.changegroup(pullop.fetch, 'pull')
+ elif not pullop.remote.capable('changegroupsubset'):
+ raise util.Abort(_("partial pull cannot be done because "
+ "other repository doesn't support "
+ "changegroupsubset."))
+ else:
+ cg = pullop.remote.changegroupsubset(pullop.fetch, pullop.heads, 'pull')
+ return pullop.repo.addchangegroup(cg, 'pull', pullop.remote.url())
+
+def _pullphase(pullop):
+ # Get remote phases data from remote
+ remotephases = pullop.remote.listkeys('phases')
+ publishing = bool(remotephases.get('publishing', False))
+ if remotephases and not publishing:
+ # remote is new and unpublishing
+ pheads, _dr = phases.analyzeremotephases(pullop.repo,
+ pullop.pulledsubset,
+ remotephases)
+ phases.advanceboundary(pullop.repo, phases.public, pheads)
+ phases.advanceboundary(pullop.repo, phases.draft,
+ pullop.pulledsubset)
+ else:
+ # Remote is old or publishing all common changesets
+ # should be seen as public
+ phases.advanceboundary(pullop.repo, phases.public,
+ pullop.pulledsubset)
+
+def _pullobsolete(pullop):
+ """utility function to pull obsolete markers from a remote
+
+ The `gettransaction` is function that return the pull transaction, creating
+ one if necessary. We return the transaction to inform the calling code that
+ a new transaction have been created (when applicable).
+
+ Exists mostly to allow overriding for experimentation purpose"""
+ tr = None
+ if obsolete._enabled:
+ pullop.repo.ui.debug('fetching remote obsolete markers\n')
+ remoteobs = pullop.remote.listkeys('obsolete')
+ if 'dump0' in remoteobs:
+ tr = pullop.gettransaction()
+ for key in sorted(remoteobs, reverse=True):
+ if key.startswith('dump'):
+ data = base85.b85decode(remoteobs[key])
+ pullop.repo.obsstore.mergemarkers(tr, data)
+ pullop.repo.invalidatevolatilesets()
+ return tr
+
--- a/mercurial/help.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/help.py Thu Feb 27 18:57:03 2014 -0600
@@ -12,18 +12,21 @@
import encoding, util, minirst
import cmdutil
-def listexts(header, exts, indent=1):
+def listexts(header, exts, indent=1, showdeprecated=False):
'''return a text listing of the given extensions'''
rst = []
if exts:
rst.append('\n%s\n\n' % header)
for name, desc in sorted(exts.iteritems()):
+ if '(DEPRECATED)' in desc and not showdeprecated:
+ continue
rst.append('%s:%s: %s\n' % (' ' * indent, name, desc))
return rst
def extshelp():
rst = loaddoc('extensions')().splitlines(True)
- rst.extend(listexts(_('enabled extensions:'), extensions.enabled()))
+ rst.extend(listexts(
+ _('enabled extensions:'), extensions.enabled(), showdeprecated=True))
rst.extend(listexts(_('disabled extensions:'), extensions.disabled()))
doc = ''.join(rst)
return doc
--- a/mercurial/help/config.txt Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/help/config.txt Thu Feb 27 18:57:03 2014 -0600
@@ -85,7 +85,9 @@
be read. Mercurial checks each of these locations in the specified
order until one or more configuration files are detected.
-.. note:: The registry key ``HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mercurial``
+.. note::
+
+ The registry key ``HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Mercurial``
is used when running 32-bit Python on 64-bit Windows.
Syntax
@@ -204,7 +206,9 @@
stable5 = latest -b stable
-.. note:: It is possible to create aliases with the same names as
+.. note::
+
+ It is possible to create aliases with the same names as
existing commands, which will then override the original
definitions. This is almost always a bad idea!
@@ -235,7 +239,9 @@
``$HG_ARGS`` expands to the arguments given to Mercurial. In the ``hg
echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
-.. note:: Some global configuration options such as ``-R`` are
+.. note::
+
+ Some global configuration options such as ``-R`` are
processed before shell aliases and will thus not be passed to
aliases.
@@ -362,7 +368,9 @@
of an empty temporary file, where the filtered data must be written by
the command.
-.. note:: The tempfile mechanism is recommended for Windows systems,
+.. note::
+
+ The tempfile mechanism is recommended for Windows systems,
where the standard shell I/O redirection operators often have
strange effects and may corrupt the contents of your files.
@@ -708,13 +716,17 @@
in ``$HG_PARENT2``. If the update succeeded, ``$HG_ERROR=0``. If the
update failed (e.g. because conflicts not resolved), ``$HG_ERROR=1``.
-.. note:: It is generally better to use standard hooks rather than the
+.. note::
+
+ It is generally better to use standard hooks rather than the
generic pre- and post- command hooks as they are guaranteed to be
called in the appropriate contexts for influencing transactions.
Also, hooks like "commit" will be called in all contexts that
generate a commit (e.g. tag) and not just the commit command.
-.. note:: Environment variables with empty values may not be passed to
+.. note::
+
+ Environment variables with empty values may not be passed to
hooks on platforms such as Windows. As an example, ``$HG_PARENT2``
will have an empty value under Unix-like platforms for non-merge
changesets, while it will not be available at all under Windows.
--- a/mercurial/help/hgignore.txt Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/help/hgignore.txt Thu Feb 27 18:57:03 2014 -0600
@@ -69,6 +69,7 @@
regexp pattern, start it with ``^``.
.. note::
+
Patterns specified in other than ``.hgignore`` are always rooted.
Please see :hg:`help patterns` for details.
--- a/mercurial/help/merge-tools.txt Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/help/merge-tools.txt Thu Feb 27 18:57:03 2014 -0600
@@ -73,6 +73,7 @@
8. The merge of the file fails and must be resolved before commit.
.. note::
+
After selecting a merge program, Mercurial will by default attempt
to merge the files using a simple merge algorithm first. Only if it doesn't
succeed because of conflicting changes Mercurial will actually execute the
--- a/mercurial/help/patterns.txt Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/help/patterns.txt Thu Feb 27 18:57:03 2014 -0600
@@ -7,6 +7,7 @@
Alternate pattern notations must be specified explicitly.
.. note::
+
Patterns specified in ``.hgignore`` are not rooted.
Please see :hg:`help hgignore` for details.
--- a/mercurial/help/phases.txt Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/help/phases.txt Thu Feb 27 18:57:03 2014 -0600
@@ -42,6 +42,7 @@
- secret changesets are neither pushed, pulled, or cloned
.. note::
+
Pulling a draft changeset from a publishing server does not mark it
as public on the server side due to the read-only nature of pull.
@@ -55,10 +56,12 @@
See :hg:`help config` for more information on configuration files.
.. note::
+
Servers running older versions of Mercurial are treated as
publishing.
.. note::
+
Changesets in secret phase are not exchanged with the server. This
applies to their content: file names, file contents, and changeset
metadata. For technical reasons, the identifier (e.g. d825e4025e39)
--- a/mercurial/help/subrepos.txt Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/help/subrepos.txt Thu Feb 27 18:57:03 2014 -0600
@@ -39,6 +39,7 @@
repositories states when committing in the parent repository.
.. note::
+
The ``.hgsubstate`` file should not be edited manually.
--- a/mercurial/help/templates.txt Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/help/templates.txt Thu Feb 27 18:57:03 2014 -0600
@@ -52,14 +52,20 @@
- if(expr, then[, else])
+- ifcontains(expr, expr, then[, else])
+
- ifeq(expr, expr, then[, else])
- join(list, sep)
- label(label, expr)
+- revset(query[, formatargs])
+
- rstdoc(text, style)
+- shortest(node)
+
- strip(text[, chars])
- sub(pat, repl, expr)
@@ -106,3 +112,11 @@
- Display the contents of the 'extra' field, one per line::
$ hg log -r 0 --template "{join(extras, '\n')}\n"
+
+- Mark the current bookmark with '*'::
+
+ $ hg log --template "{bookmarks % '{bookmark}{ifeq(bookmark, current, \"*\")} '}\n"
+
+- Mark the working copy parent with '@'::
+
+ $ hg log --template "{ifcontains(rev, revset('.'), '@')}\n"
--- a/mercurial/hgweb/webcommands.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/hgweb/webcommands.py Thu Feb 27 18:57:03 2014 -0600
@@ -187,7 +187,7 @@
mfunc = revset.match(web.repo.ui, revdef)
try:
- revs = mfunc(web.repo, list(web.repo))
+ revs = mfunc(web.repo, revset.baseset(web.repo))
return MODE_REVSET, revs
# ParseError: wrongly placed tokens, wrongs arguments, etc
# RepoLookupError: no such revision, e.g. in 'revision:'
--- a/mercurial/hook.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/hook.py Thu Feb 27 18:57:03 2014 -0600
@@ -6,7 +6,7 @@
# GNU General Public License version 2 or any later version.
from i18n import _
-import os, sys, time, types
+import os, sys, time
import extensions, util, demandimport
def _pythonhook(ui, repo, name, hname, funcname, args, throw):
@@ -19,11 +19,10 @@
unmodified commands (e.g. mercurial.commands.update) can
be run as hooks without wrappers to convert return values.'''
- ui.note(_("calling hook %s: %s\n") % (hname, funcname))
- starttime = time.time()
-
- obj = funcname
- if not util.safehasattr(obj, '__call__'):
+ if util.safehasattr(funcname, '__call__'):
+ obj = funcname
+ funcname = obj.__module__ + "." + obj.__name__
+ else:
d = funcname.rfind('.')
if d == -1:
raise util.Abort(_('%s hook is invalid ("%s" not in '
@@ -75,6 +74,10 @@
raise util.Abort(_('%s hook is invalid '
'("%s" is not callable)') %
(hname, funcname))
+
+ ui.note(_("calling hook %s: %s\n") % (hname, funcname))
+ starttime = time.time()
+
try:
try:
# redirect IO descriptors to the ui descriptors so hooks
@@ -100,11 +103,8 @@
finally:
sys.stdout, sys.stderr, sys.stdin = old
duration = time.time() - starttime
- readablefunc = funcname
- if isinstance(funcname, types.FunctionType):
- readablefunc = funcname.__module__ + "." + funcname.__name__
ui.log('pythonhook', 'pythonhook-%s: %s finished in %0.2f seconds\n',
- name, readablefunc, duration)
+ name, funcname, duration)
if r:
if throw:
raise util.Abort(_('%s hook failed') % hname)
--- a/mercurial/localrepo.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/localrepo.py Thu Feb 27 18:57:03 2014 -0600
@@ -9,7 +9,7 @@
import peer, changegroup, subrepo, discovery, pushkey, obsolete, repoview
import changelog, dirstate, filelog, manifest, context, bookmarks, phases
import lock as lockmod
-import transaction, store, encoding
+import transaction, store, encoding, exchange
import scmutil, util, extensions, hook, error, revset
import match as matchmod
import merge as mergemod
@@ -428,7 +428,7 @@
'''Return a list of revisions matching the given revset'''
expr = revset.formatspec(expr, *args)
m = revset.match(None, expr)
- return [r for r in m(self, list(self))]
+ return revset.baseset([r for r in m(self, revset.baseset(self))])
def set(self, expr, *args):
'''
@@ -1005,6 +1005,7 @@
l = lockmod.lock(vfs, lockname,
int(self.ui.config("ui", "timeout", "600")),
releasefn, desc=desc)
+ self.ui.warn(_("got lock after %s seconds\n") % l.delay)
if acquirefn:
acquirefn()
return l
@@ -1122,6 +1123,8 @@
self.ui.warn(_("warning: can't find ancestor for '%s' "
"copied from '%s'!\n") % (fname, cfname))
+ elif fparent1 == nullid:
+ fparent1, fparent2 = fparent2, nullid
elif fparent2 != nullid:
# is one parent an ancestor of the other?
fparentancestor = flog.ancestor(fparent1, fparent2)
@@ -1578,7 +1581,7 @@
r = modified, added, removed, deleted, unknown, ignored, clean
if listsubrepos:
- for subpath, sub in subrepo.itersubrepos(ctx1, ctx2):
+ for subpath, sub in scmutil.itersubrepos(ctx1, ctx2):
if working:
rev2 = None
else:
@@ -1658,89 +1661,7 @@
return r
def pull(self, remote, heads=None, force=False):
- if remote.local():
- missing = set(remote.requirements) - self.supported
- if missing:
- msg = _("required features are not"
- " supported in the destination:"
- " %s") % (', '.join(sorted(missing)))
- raise util.Abort(msg)
-
- # don't open transaction for nothing or you break future useful
- # rollback call
- tr = None
- trname = 'pull\n' + util.hidepassword(remote.url())
- lock = self.lock()
- try:
- tmp = discovery.findcommonincoming(self.unfiltered(), remote,
- heads=heads, force=force)
- common, fetch, rheads = tmp
- if not fetch:
- self.ui.status(_("no changes found\n"))
- result = 0
- else:
- tr = self.transaction(trname)
- if heads is None and list(common) == [nullid]:
- self.ui.status(_("requesting all changes\n"))
- elif heads is None and remote.capable('changegroupsubset'):
- # issue1320, avoid a race if remote changed after discovery
- heads = rheads
-
- if remote.capable('getbundle'):
- # TODO: get bundlecaps from remote
- cg = remote.getbundle('pull', common=common,
- heads=heads or rheads)
- elif heads is None:
- cg = remote.changegroup(fetch, 'pull')
- elif not remote.capable('changegroupsubset'):
- raise util.Abort(_("partial pull cannot be done because "
- "other repository doesn't support "
- "changegroupsubset."))
- else:
- cg = remote.changegroupsubset(fetch, heads, 'pull')
- result = self.addchangegroup(cg, 'pull', remote.url())
-
- # compute target subset
- if heads is None:
- # We pulled every thing possible
- # sync on everything common
- subset = common + rheads
- else:
- # We pulled a specific subset
- # sync on this subset
- subset = heads
-
- # Get remote phases data from remote
- remotephases = remote.listkeys('phases')
- publishing = bool(remotephases.get('publishing', False))
- if remotephases and not publishing:
- # remote is new and unpublishing
- pheads, _dr = phases.analyzeremotephases(self, subset,
- remotephases)
- phases.advanceboundary(self, phases.public, pheads)
- phases.advanceboundary(self, phases.draft, subset)
- else:
- # Remote is old or publishing all common changesets
- # should be seen as public
- phases.advanceboundary(self, phases.public, subset)
-
- def gettransaction():
- if tr is None:
- return self.transaction(trname)
- return tr
-
- obstr = obsolete.syncpull(self, remote, gettransaction)
- if obstr is not None:
- tr = obstr
-
- if tr is not None:
- tr.close()
- finally:
- if tr is not None:
- tr.release()
- lock.release()
-
- return result
+ return exchange.pull (self, remote, heads, force)
def checkpush(self, force, revs):
"""Extensions can override this function if additional checks have
@@ -1750,223 +1671,7 @@
pass
def push(self, remote, force=False, revs=None, newbranch=False):
- '''Push outgoing changesets (limited by revs) from the current
- repository to remote. Return an integer:
- - None means nothing to push
- - 0 means HTTP error
- - 1 means we pushed and remote head count is unchanged *or*
- we have outgoing changesets but refused to push
- - other values as described by addchangegroup()
- '''
- if remote.local():
- missing = set(self.requirements) - remote.local().supported
- if missing:
- msg = _("required features are not"
- " supported in the destination:"
- " %s") % (', '.join(sorted(missing)))
- raise util.Abort(msg)
-
- # there are two ways to push to remote repo:
- #
- # addchangegroup assumes local user can lock remote
- # repo (local filesystem, old ssh servers).
- #
- # unbundle assumes local user cannot lock remote repo (new ssh
- # servers, http servers).
-
- if not remote.canpush():
- raise util.Abort(_("destination does not support push"))
- unfi = self.unfiltered()
- def localphasemove(nodes, phase=phases.public):
- """move <nodes> to <phase> in the local source repo"""
- if locallock is not None:
- phases.advanceboundary(self, phase, nodes)
- else:
- # repo is not locked, do not change any phases!
- # Informs the user that phases should have been moved when
- # applicable.
- actualmoves = [n for n in nodes if phase < self[n].phase()]
- phasestr = phases.phasenames[phase]
- if actualmoves:
- self.ui.status(_('cannot lock source repo, skipping local'
- ' %s phase update\n') % phasestr)
- # get local lock as we might write phase data
- locallock = None
- try:
- locallock = self.lock()
- except IOError, err:
- if err.errno != errno.EACCES:
- raise
- # source repo cannot be locked.
- # We do not abort the push, but just disable the local phase
- # synchronisation.
- msg = 'cannot lock source repository: %s\n' % err
- self.ui.debug(msg)
- try:
- self.checkpush(force, revs)
- lock = None
- unbundle = remote.capable('unbundle')
- if not unbundle:
- lock = remote.lock()
- try:
- # discovery
- fci = discovery.findcommonincoming
- commoninc = fci(unfi, remote, force=force)
- common, inc, remoteheads = commoninc
- fco = discovery.findcommonoutgoing
- outgoing = fco(unfi, remote, onlyheads=revs,
- commoninc=commoninc, force=force)
-
-
- if not outgoing.missing:
- # nothing to push
- scmutil.nochangesfound(unfi.ui, unfi, outgoing.excluded)
- ret = None
- else:
- # something to push
- if not force:
- # if self.obsstore == False --> no obsolete
- # then, save the iteration
- if unfi.obsstore:
- # this message are here for 80 char limit reason
- mso = _("push includes obsolete changeset: %s!")
- mst = "push includes %s changeset: %s!"
- # plain versions for i18n tool to detect them
- _("push includes unstable changeset: %s!")
- _("push includes bumped changeset: %s!")
- _("push includes divergent changeset: %s!")
- # If we are to push if there is at least one
- # obsolete or unstable changeset in missing, at
- # least one of the missinghead will be obsolete or
- # unstable. So checking heads only is ok
- for node in outgoing.missingheads:
- ctx = unfi[node]
- if ctx.obsolete():
- raise util.Abort(mso % ctx)
- elif ctx.troubled():
- raise util.Abort(_(mst)
- % (ctx.troubles()[0],
- ctx))
- newbm = self.ui.configlist('bookmarks', 'pushing')
- discovery.checkheads(unfi, remote, outgoing,
- remoteheads, newbranch,
- bool(inc), newbm)
-
- # TODO: get bundlecaps from remote
- bundlecaps = None
- # create a changegroup from local
- if revs is None and not (outgoing.excluded
- or self.changelog.filteredrevs):
- # push everything,
- # use the fast path, no race possible on push
- bundler = changegroup.bundle10(self, bundlecaps)
- cg = self._changegroupsubset(outgoing,
- bundler,
- 'push',
- fastpath=True)
- else:
- cg = self.getlocalbundle('push', outgoing, bundlecaps)
-
- # apply changegroup to remote
- if unbundle:
- # local repo finds heads on server, finds out what
- # revs it must push. once revs transferred, if server
- # finds it has different heads (someone else won
- # commit/push race), server aborts.
- if force:
- remoteheads = ['force']
- # ssh: return remote's addchangegroup()
- # http: return remote's addchangegroup() or 0 for error
- ret = remote.unbundle(cg, remoteheads, 'push')
- else:
- # we return an integer indicating remote head count
- # change
- ret = remote.addchangegroup(cg, 'push', self.url())
-
- if ret:
- # push succeed, synchronize target of the push
- cheads = outgoing.missingheads
- elif revs is None:
- # All out push fails. synchronize all common
- cheads = outgoing.commonheads
- else:
- # I want cheads = heads(::missingheads and ::commonheads)
- # (missingheads is revs with secret changeset filtered out)
- #
- # This can be expressed as:
- # cheads = ( (missingheads and ::commonheads)
- # + (commonheads and ::missingheads))"
- # )
- #
- # while trying to push we already computed the following:
- # common = (::commonheads)
- # missing = ((commonheads::missingheads) - commonheads)
- #
- # We can pick:
- # * missingheads part of common (::commonheads)
- common = set(outgoing.common)
- nm = self.changelog.nodemap
- cheads = [node for node in revs if nm[node] in common]
- # and
- # * commonheads parents on missing
- revset = unfi.set('%ln and parents(roots(%ln))',
- outgoing.commonheads,
- outgoing.missing)
- cheads.extend(c.node() for c in revset)
- # even when we don't push, exchanging phase data is useful
- remotephases = remote.listkeys('phases')
- if (self.ui.configbool('ui', '_usedassubrepo', False)
- and remotephases # server supports phases
- and ret is None # nothing was pushed
- and remotephases.get('publishing', False)):
- # When:
- # - this is a subrepo push
- # - and remote support phase
- # - and no changeset was pushed
- # - and remote is publishing
- # We may be in issue 3871 case!
- # We drop the possible phase synchronisation done by
- # courtesy to publish changesets possibly locally draft
- # on the remote.
- remotephases = {'publishing': 'True'}
- if not remotephases: # old server or public only repo
- localphasemove(cheads)
- # don't push any phase data as there is nothing to push
- else:
- ana = phases.analyzeremotephases(self, cheads, remotephases)
- pheads, droots = ana
- ### Apply remote phase on local
- if remotephases.get('publishing', False):
- localphasemove(cheads)
- else: # publish = False
- localphasemove(pheads)
- localphasemove(cheads, phases.draft)
- ### Apply local phase on remote
-
- # Get the list of all revs draft on remote by public here.
- # XXX Beware that revset break if droots is not strictly
- # XXX root we may want to ensure it is but it is costly
- outdated = unfi.set('heads((%ln::%ln) and public())',
- droots, cheads)
- for newremotehead in outdated:
- r = remote.pushkey('phases',
- newremotehead.hex(),
- str(phases.draft),
- str(phases.public))
- if not r:
- self.ui.warn(_('updating %s to public failed!\n')
- % newremotehead)
- self.ui.debug('try to push obsolete markers to remote\n')
- obsolete.syncpush(self, remote)
- finally:
- if lock is not None:
- lock.release()
- finally:
- if locallock is not None:
- locallock.release()
-
- bookmarks.updateremote(self.ui, unfi, remote, revs)
- return ret
+ return exchange.push(self, remote, force, revs, newbranch)
def changegroupinfo(self, nodes, source):
if self.ui.verbose or source == 'bundle':
@@ -1976,9 +1681,9 @@
for node in nodes:
self.ui.debug("%s\n" % hex(node))
- def changegroupsubset(self, bases, heads, source):
+ def changegroupsubset(self, roots, heads, source):
"""Compute a changegroup consisting of all the nodes that are
- descendants of any of the bases and ancestors of any of the heads.
+ descendants of any of the roots and ancestors of any of the heads.
Return a chunkbuffer object whose read() method will return
successive changegroup chunks.
@@ -1990,12 +1695,12 @@
the changegroup a particular filenode or manifestnode belongs to.
"""
cl = self.changelog
- if not bases:
- bases = [nullid]
+ if not roots:
+ roots = [nullid]
# TODO: remove call to nodesbetween.
- csets, bases, heads = cl.nodesbetween(bases, heads)
+ csets, roots, heads = cl.nodesbetween(roots, heads)
discbases = []
- for n in bases:
+ for n in roots:
discbases.extend([p for p in cl.parents(n) if p != nullid])
outgoing = discovery.outgoing(cl, discbases, heads)
bundler = changegroup.bundle10(self)
@@ -2176,9 +1881,9 @@
added = [cl.node(r) for r in xrange(clstart, clend)]
publishing = self.ui.configbool('phases', 'publish', True)
if srctype == 'push':
- # Old server can not push the boundary themself.
- # New server won't push the boundary if changeset already
- # existed locally as secrete
+ # Old servers can not push the boundary themselves.
+ # New servers won't push the boundary if changeset already
+ # exists locally as secret
#
# We should not use added here but the list of all change in
# the bundle
--- a/mercurial/lock.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/lock.py Thu Feb 27 18:57:03 2014 -0600
@@ -38,7 +38,7 @@
self.desc = desc
self.postrelease = []
self.pid = os.getpid()
- self.lock()
+ self.delay = self.lock()
def __del__(self):
if self.held:
@@ -57,7 +57,7 @@
while True:
try:
self.trylock()
- return 1
+ return self.timeout - timeout
except error.LockHeld, inst:
if timeout != 0:
time.sleep(1)
--- a/mercurial/match.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/match.py Thu Feb 27 18:57:03 2014 -0600
@@ -6,7 +6,7 @@
# GNU General Public License version 2 or any later version.
import re
-import util, fileset, pathutil
+import util, pathutil
from i18n import _
def _rematcher(pat):
@@ -26,7 +26,7 @@
if kind == 'set':
if not ctx:
raise util.Abort("fileset expression with no context")
- s = fileset.getfileset(ctx, expr)
+ s = ctx.getfileset(expr)
fset.update(s)
continue
other.append((kind, expr))
--- a/mercurial/minirst.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/minirst.py Thu Feb 27 18:57:03 2014 -0600
@@ -109,7 +109,7 @@
elif len(blocks[i]['lines']) == 1 and \
blocks[i]['lines'][0].lstrip(' ').startswith('.. ') and \
blocks[i]['lines'][0].find(' ', 3) == -1:
- # directive on its onw line, not a literal block
+ # directive on its own line, not a literal block
i += 1
continue
else:
@@ -382,7 +382,7 @@
blocks[i]['type'] in ('bullet', 'option', 'field')):
i += 1
elif not blocks[i - 1]['lines']:
- # no lines in previous block, do not seperate
+ # no lines in previous block, do not separate
i += 1
else:
blocks.insert(i, dict(lines=[''], indent=0, type='margin'))
--- a/mercurial/obsolete.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/obsolete.py Thu Feb 27 18:57:03 2014 -0600
@@ -247,6 +247,9 @@
def __iter__(self):
return iter(self._all)
+ def __len__(self):
+ return len(self._all)
+
def __nonzero__(self):
return bool(self._all)
@@ -256,6 +259,12 @@
* ensuring it is hashable
* check mandatory metadata
* encode metadata
+
+ If you are a human writing code creating marker you want to use the
+ `createmarkers` function in this module instead.
+
+ return True if a new marker have been added, False if the markers
+ already existed (no op).
"""
if metadata is None:
metadata = {}
@@ -267,7 +276,7 @@
if len(succ) != 20:
raise ValueError(succ)
marker = (str(prec), tuple(succs), int(flag), encodemeta(metadata))
- self.add(transaction, [marker])
+ return bool(self.add(transaction, [marker]))
def add(self, transaction, markers):
"""Add new markers to the store
@@ -384,43 +393,6 @@
finally:
lock.release()
-def syncpush(repo, remote):
- """utility function to push obsolete markers to a remote
-
- Exist mostly to allow overriding for experimentation purpose"""
- if (_enabled and repo.obsstore and
- 'obsolete' in remote.listkeys('namespaces')):
- rslts = []
- remotedata = repo.listkeys('obsolete')
- for key in sorted(remotedata, reverse=True):
- # reverse sort to ensure we end with dump0
- data = remotedata[key]
- rslts.append(remote.pushkey('obsolete', key, '', data))
- if [r for r in rslts if not r]:
- msg = _('failed to push some obsolete markers!\n')
- repo.ui.warn(msg)
-
-def syncpull(repo, remote, gettransaction):
- """utility function to pull obsolete markers from a remote
-
- The `gettransaction` is function that return the pull transaction, creating
- one if necessary. We return the transaction to inform the calling code that
- a new transaction have been created (when applicable).
-
- Exists mostly to allow overriding for experimentation purpose"""
- tr = None
- if _enabled:
- repo.ui.debug('fetching remote obsolete markers\n')
- remoteobs = remote.listkeys('obsolete')
- if 'dump0' in remoteobs:
- tr = gettransaction()
- for key in sorted(remoteobs, reverse=True):
- if key.startswith('dump'):
- data = base85.b85decode(remoteobs[key])
- repo.obsstore.mergemarkers(tr, data)
- repo.invalidatevolatilesets()
- return tr
-
def allmarkers(repo):
"""all obsolete markers known in a repository"""
for markerdata in repo.obsstore:
@@ -800,7 +772,7 @@
def _computebumpedset(repo):
"""the set of revs trying to obsolete public revisions"""
bumped = set()
- # utils function (avoid attribut lookup in the loop)
+ # utils function (avoid attribute lookup in the loop)
phase = repo._phasecache.phase # would be faster to grab the full list
public = phases.public
cl = repo.changelog
@@ -845,8 +817,10 @@
def createmarkers(repo, relations, flag=0, metadata=None):
"""Add obsolete markers between changesets in a repo
- <relations> must be an iterable of (<old>, (<new>, ...)) tuple.
- `old` and `news` are changectx.
+ <relations> must be an iterable of (<old>, (<new>, ...)[,{metadata}])
+ tuple. `old` and `news` are changectx. metadata is an optional dictionnary
+ containing metadata for this marker only. It is merged with the global
+ metadata specified through the `metadata` argument of this function,
Trying to obsolete a public changeset will raise an exception.
@@ -865,7 +839,13 @@
metadata['user'] = repo.ui.username()
tr = repo.transaction('add-obsolescence-marker')
try:
- for prec, sucs in relations:
+ for rel in relations:
+ prec = rel[0]
+ sucs = rel[1]
+ localmetadata = metadata.copy()
+ if 2 < len(rel):
+ localmetadata.update(rel[2])
+
if not prec.mutable():
raise util.Abort("cannot obsolete immutable changeset: %s"
% prec)
@@ -873,7 +853,7 @@
nsucs = tuple(s.node() for s in sucs)
if nprec in nsucs:
raise util.Abort("changeset %s cannot obsolete itself" % prec)
- repo.obsstore.create(tr, nprec, nsucs, flag, metadata)
+ repo.obsstore.create(tr, nprec, nsucs, flag, localmetadata)
repo.filteredrevcache.clear()
tr.close()
finally:
--- a/mercurial/parsers.c Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/parsers.c Thu Feb 27 18:57:03 2014 -0600
@@ -1208,7 +1208,7 @@
const bitmask allseen = (1ull << revcount) - 1;
const bitmask poison = 1ull << revcount;
PyObject *gca = PyList_New(0);
- int i, v, interesting, left;
+ int i, v, interesting;
int maxrev = -1;
long sp;
bitmask *seen;
@@ -1230,7 +1230,7 @@
for (i = 0; i < revcount; i++)
seen[revs[i]] = 1ull << i;
- interesting = left = revcount;
+ interesting = revcount;
for (v = maxrev; v >= 0 && interesting; v--) {
long sv = seen[v];
@@ -1251,11 +1251,8 @@
}
sv |= poison;
for (i = 0; i < revcount; i++) {
- if (revs[i] == v) {
- if (--left <= 1)
- goto done;
- break;
- }
+ if (revs[i] == v)
+ goto done;
}
}
}
@@ -1529,10 +1526,6 @@
ret = gca;
Py_INCREF(gca);
}
- else if (PyList_GET_SIZE(gca) == 1) {
- ret = PyList_GET_ITEM(gca, 0);
- Py_INCREF(ret);
- }
else ret = find_deepest(self, gca);
done:
--- a/mercurial/phases.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/phases.py Thu Feb 27 18:57:03 2014 -0600
@@ -258,7 +258,7 @@
filtered = False
nodemap = repo.changelog.nodemap # to filter unknown nodes
for phase, nodes in enumerate(self.phaseroots):
- missing = [node for node in nodes if node not in nodemap]
+ missing = sorted(node for node in nodes if node not in nodemap)
if missing:
for mnode in missing:
repo.ui.debug(
--- a/mercurial/repoview.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/repoview.py Thu Feb 27 18:57:03 2014 -0600
@@ -9,7 +9,7 @@
import copy
import phases
import util
-import obsolete, revset
+import obsolete
def hideablerevs(repo):
@@ -28,8 +28,9 @@
cl = repo.changelog
firsthideable = min(hideable)
revs = cl.revs(start=firsthideable)
- blockers = [r for r in revset._children(repo, revs, hideable)
- if r not in hideable]
+ tofilter = repo.revs(
+ '(%ld) and children(%ld)', list(revs), list(hideable))
+ blockers = [r for r in tofilter if r not in hideable]
for par in repo[None].parents():
blockers.append(par.rev())
for bm in repo._bookmarks.values():
@@ -95,7 +96,7 @@
# function to compute filtered set
#
-# When addding a new filter you MUST update the table at:
+# When adding a new filter you MUST update the table at:
# mercurial.branchmap.subsettable
# Otherwise your filter will have to recompute all its branches cache
# from scratch (very slow).
--- a/mercurial/revlog.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/revlog.py Thu Feb 27 18:57:03 2014 -0600
@@ -734,17 +734,21 @@
break
return False
- def ancestor(self, a, b):
- """calculate the least common ancestor of nodes a and b"""
-
+ def commonancestors(self, a, b):
+ """calculate the least common ancestors of nodes a and b"""
a, b = self.rev(a), self.rev(b)
try:
ancs = self.index.ancestors(a, b)
- except (AttributeError, OverflowError):
+ except (AttributeError, OverflowError): # C implementation failed
ancs = ancestor.ancestors(self.parentrevs, a, b)
+ return map(self.node, ancs)
+
+ def ancestor(self, a, b):
+ """calculate a least common ancestor of nodes a and b"""
+ ancs = self.commonancestors(a, b)
if ancs:
# choose a consistent winner when there's a tie
- return min(map(self.node, ancs))
+ return min(ancs)
return nullid
def _match(self, id):
--- a/mercurial/revset.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/revset.py Thu Feb 27 18:57:03 2014 -0600
@@ -53,9 +53,9 @@
"""Return all paths between roots and heads, inclusive of both endpoint
sets."""
if not roots:
- return []
+ return baseset([])
parentrevs = repo.changelog.parentrevs
- visit = heads[:]
+ visit = baseset(heads)
reachable = set()
seen = {}
minroot = min(roots)
@@ -72,12 +72,12 @@
if parent >= minroot and parent not in seen:
visit.append(parent)
if not reachable:
- return []
+ return baseset([])
for rev in sorted(seen):
for parent in seen[rev]:
if parent in reachable:
reachable.add(rev)
- return sorted(reachable)
+ return baseset(sorted(reachable))
elements = {
"(": (20, ("group", 1, ")"), ("func", 1, ")")),
@@ -195,7 +195,10 @@
def getset(repo, subset, x):
if not x:
raise error.ParseError(_("missing argument"))
- return methods[x[0]](repo, subset, *x[1:])
+ s = methods[x[0]](repo, subset, *x[1:])
+ if util.safehasattr(s, 'set'):
+ return s
+ return baseset(s)
def _getrevsource(repo, r):
extra = repo[r].extra()
@@ -212,10 +215,10 @@
def stringset(repo, subset, x):
x = repo[x].rev()
if x == -1 and len(subset) == len(repo):
- return [-1]
+ return baseset([-1])
if len(subset) == len(repo) or x in subset:
- return [x]
- return []
+ return baseset([x])
+ return baseset([])
def symbolset(repo, subset, x):
if x in symbols:
@@ -223,39 +226,36 @@
return stringset(repo, subset, x)
def rangeset(repo, subset, x, y):
- cl = repo.changelog
+ cl = baseset(repo.changelog)
m = getset(repo, cl, x)
n = getset(repo, cl, y)
if not m or not n:
- return []
+ return baseset([])
m, n = m[0], n[-1]
if m < n:
- r = range(m, n + 1)
+ r = spanset(repo, m, n + 1)
else:
- r = range(m, n - 1, -1)
- s = set(subset)
- return [x for x in r if x in s]
+ r = spanset(repo, m, n - 1)
+ return r & subset
def dagrange(repo, subset, x, y):
- r = list(repo)
+ r = spanset(repo)
xs = _revsbetween(repo, getset(repo, r, x), getset(repo, r, y))
- s = set(subset)
- return [r for r in xs if r in s]
+ s = subset.set()
+ return baseset([r for r in xs if r in s])
def andset(repo, subset, x, y):
return getset(repo, getset(repo, subset, x), y)
def orset(repo, subset, x, y):
xl = getset(repo, subset, x)
- s = set(xl)
- yl = getset(repo, [r for r in subset if r not in s], y)
+ yl = getset(repo, subset - xl, y)
return xl + yl
def notset(repo, subset, x):
- s = set(getset(repo, subset, x))
- return [r for r in subset if r not in s]
+ return subset - getset(repo, subset, x)
def listset(repo, subset, a, b):
raise error.ParseError(_("can't use a list in this context"))
@@ -289,7 +289,7 @@
"""
# i18n: "ancestor" is a keyword
l = getlist(x)
- rl = list(repo)
+ rl = spanset(repo)
anc = None
# (getset(repo, rl, i) for i in l) generates a list of lists
@@ -304,15 +304,15 @@
anc = rev(ancestor(node(anc), node(r)))
if anc is not None and anc in subset:
- return [anc]
- return []
+ return baseset([anc])
+ return baseset([])
def _ancestors(repo, subset, x, followfirst=False):
- args = getset(repo, list(repo), x)
+ args = getset(repo, spanset(repo), x)
if not args:
- return []
+ return baseset([])
s = set(_revancestors(repo, args, followfirst)) | set(args)
- return [r for r in subset if r in s]
+ return baseset([r for r in subset if r in s])
def ancestors(repo, subset, x):
"""``ancestors(set)``
@@ -336,11 +336,11 @@
raise error.ParseError(_("~ expects a number"))
ps = set()
cl = repo.changelog
- for r in getset(repo, cl, x):
+ for r in getset(repo, baseset(cl), x):
for i in range(n):
r = cl.parentrevs(r)[0]
ps.add(r)
- return [r for r in subset if r in ps]
+ return baseset([r for r in subset if r in ps])
def author(repo, subset, x):
"""``author(string)``
@@ -349,7 +349,7 @@
# i18n: "author" is a keyword
n = encoding.lower(getstring(x, _("author requires a string")))
kind, pattern, matcher = _substringmatcher(n)
- return [r for r in subset if matcher(encoding.lower(repo[r].user()))]
+ return lazyset(subset, lambda x: matcher(encoding.lower(repo[x].user())))
def bisect(repo, subset, x):
"""``bisect(string)``
@@ -366,7 +366,7 @@
# i18n: "bisect" is a keyword
status = getstring(x, _("bisect requires a string")).lower()
state = set(hbisect.get(repo, status))
- return [r for r in subset if r in state]
+ return baseset([r for r in subset if r in state])
# Backward-compatibility
# - no help entry so that we do not advertise it any more
@@ -393,7 +393,7 @@
if not bmrev:
raise util.Abort(_("bookmark '%s' does not exist") % bm)
bmrev = repo[bmrev].rev()
- return [r for r in subset if r == bmrev]
+ return lazyset(subset, lambda r: r == bmrev)
else:
matchrevs = set()
for name, bmrev in repo._bookmarks.iteritems():
@@ -405,11 +405,11 @@
bmrevs = set()
for bmrev in matchrevs:
bmrevs.add(repo[bmrev].rev())
- return [r for r in subset if r in bmrevs]
+ return subset & bmrevs
bms = set([repo[r].rev()
for r in repo._bookmarks.values()])
- return [r for r in subset if r in bms]
+ return lazyset(subset, lambda r: r in bms)
def branch(repo, subset, x):
"""``branch(string or set)``
@@ -431,16 +431,16 @@
# note: falls through to the revspec case if no branch with
# this name exists
if pattern in repo.branchmap():
- return [r for r in subset if matcher(repo[r].branch())]
+ return lazyset(subset, lambda r: matcher(repo[r].branch()))
else:
- return [r for r in subset if matcher(repo[r].branch())]
+ return lazyset(subset, lambda r: matcher(repo[r].branch()))
- s = getset(repo, list(repo), x)
+ s = getset(repo, spanset(repo), x)
b = set()
for r in s:
b.add(repo[r].branch())
- s = set(s)
- return [r for r in subset if r in s or repo[r].branch() in b]
+ s = s.set()
+ return lazyset(subset, lambda r: r in s or repo[r].branch() in b)
def bumped(repo, subset, x):
"""``bumped()``
@@ -451,7 +451,7 @@
# i18n: "bumped" is a keyword
getargs(x, 0, 0, _("bumped takes no arguments"))
bumped = obsmod.getrevs(repo, 'bumped')
- return [r for r in subset if r in bumped]
+ return subset & bumped
def bundle(repo, subset, x):
"""``bundle()``
@@ -463,43 +463,43 @@
bundlerevs = repo.changelog.bundlerevs
except AttributeError:
raise util.Abort(_("no bundle provided - specify with -R"))
- return [r for r in subset if r in bundlerevs]
+ return subset & bundlerevs
def checkstatus(repo, subset, pat, field):
- m = None
- s = []
hasset = matchmod.patkind(pat) == 'set'
- fname = None
- for r in subset:
- c = repo[r]
+
+ def matches(x):
+ m = None
+ fname = None
+ c = repo[x]
if not m or hasset:
m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
if not m.anypats() and len(m.files()) == 1:
fname = m.files()[0]
if fname is not None:
if fname not in c.files():
- continue
+ return False
else:
for f in c.files():
if m(f):
break
else:
- continue
+ return False
files = repo.status(c.p1().node(), c.node())[field]
if fname is not None:
if fname in files:
- s.append(r)
+ return True
else:
for f in files:
if m(f):
- s.append(r)
- break
- return s
+ return True
+
+ return lazyset(subset, matches)
def _children(repo, narrow, parentset):
cs = set()
if not parentset:
- return cs
+ return baseset(cs)
pr = repo.changelog.parentrevs
minrev = min(parentset)
for r in narrow:
@@ -508,15 +508,15 @@
for p in pr(r):
if p in parentset:
cs.add(r)
- return cs
+ return baseset(cs)
def children(repo, subset, x):
"""``children(set)``
Child changesets of changesets in set.
"""
- s = set(getset(repo, list(repo), x))
+ s = getset(repo, baseset(repo), x).set()
cs = _children(repo, subset, s)
- return [r for r in subset if r in cs]
+ return subset & cs
def closed(repo, subset, x):
"""``closed()``
@@ -524,7 +524,7 @@
"""
# i18n: "closed" is a keyword
getargs(x, 0, 0, _("closed takes no arguments"))
- return [r for r in subset if repo[r].closesbranch()]
+ return lazyset(subset, lambda r: repo[r].closesbranch())
def contains(repo, subset, x):
"""``contains(pattern)``
@@ -537,23 +537,21 @@
"""
# i18n: "contains" is a keyword
pat = getstring(x, _("contains requires a pattern"))
- s = []
- if not matchmod.patkind(pat):
- pat = pathutil.canonpath(repo.root, repo.getcwd(), pat)
- for r in subset:
- if pat in repo[r]:
- s.append(r)
- else:
- m = None
- for r in subset:
- c = repo[r]
- if not m or matchmod.patkind(pat) == 'set':
- m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
+
+ def matches(x):
+ if not matchmod.patkind(pat):
+ pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
+ if pats in repo[x]:
+ return True
+ else:
+ c = repo[x]
+ m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
for f in c.manifest():
if m(f):
- s.append(r)
- break
- return s
+ return True
+ return False
+
+ return lazyset(subset, matches)
def converted(repo, subset, x):
"""``converted([id])``
@@ -575,7 +573,7 @@
source = repo[r].extra().get('convert_revision', None)
return source is not None and (rev is None or source.startswith(rev))
- return [r for r in subset if _matchvalue(r)]
+ return lazyset(subset, lambda r: _matchvalue(r))
def date(repo, subset, x):
"""``date(interval)``
@@ -584,7 +582,7 @@
# i18n: "date" is a keyword
ds = getstring(x, _("date requires a string"))
dm = util.matchdate(ds)
- return [r for r in subset if dm(repo[r].date()[0])]
+ return lazyset(subset, lambda x: dm(repo[x].date()[0]))
def desc(repo, subset, x):
"""``desc(string)``
@@ -592,19 +590,19 @@
"""
# i18n: "desc" is a keyword
ds = encoding.lower(getstring(x, _("desc requires a string")))
- l = []
- for r in subset:
- c = repo[r]
- if ds in encoding.lower(c.description()):
- l.append(r)
- return l
+
+ def matches(x):
+ c = repo[x]
+ return ds in encoding.lower(c.description())
+
+ return lazyset(subset, matches)
def _descendants(repo, subset, x, followfirst=False):
- args = getset(repo, list(repo), x)
+ args = getset(repo, spanset(repo), x)
if not args:
- return []
+ return baseset([])
s = set(_revdescendants(repo, args, followfirst)) | set(args)
- return [r for r in subset if r in s]
+ return subset & s
def descendants(repo, subset, x):
"""``descendants(set)``
@@ -624,9 +622,9 @@
is the same as passing all().
"""
if x is not None:
- args = set(getset(repo, list(repo), x))
+ args = getset(repo, spanset(repo), x).set()
else:
- args = set(getall(repo, list(repo), x))
+ args = getall(repo, spanset(repo), x).set()
dests = set()
@@ -659,7 +657,7 @@
r = src
src = _getrevsource(repo, r)
- return [r for r in subset if r in dests]
+ return baseset([r for r in subset if r in dests])
def divergent(repo, subset, x):
"""``divergent()``
@@ -668,7 +666,7 @@
# i18n: "divergent" is a keyword
getargs(x, 0, 0, _("divergent takes no arguments"))
divergent = obsmod.getrevs(repo, 'divergent')
- return [r for r in subset if r in divergent]
+ return baseset([r for r in subset if r in divergent])
def draft(repo, subset, x):
"""``draft()``
@@ -676,7 +674,7 @@
# i18n: "draft" is a keyword
getargs(x, 0, 0, _("draft takes no arguments"))
pc = repo._phasecache
- return [r for r in subset if pc.phase(repo, r) == phases.draft]
+ return lazyset(subset, lambda r: pc.phase(repo, r) == phases.draft)
def extinct(repo, subset, x):
"""``extinct()``
@@ -685,7 +683,7 @@
# i18n: "extinct" is a keyword
getargs(x, 0, 0, _("extinct takes no arguments"))
extincts = obsmod.getrevs(repo, 'extinct')
- return [r for r in subset if r in extincts]
+ return subset & extincts
def extra(repo, subset, x):
"""``extra(label, [value])``
@@ -712,7 +710,7 @@
extra = repo[r].extra()
return label in extra and (value is None or matcher(extra[label]))
- return [r for r in subset if _matchvalue(r)]
+ return lazyset(subset, lambda r: _matchvalue(r))
def filelog(repo, subset, x):
"""``filelog(pattern)``
@@ -744,7 +742,7 @@
for fr in fl:
s.add(fl.linkrev(fr))
- return [r for r in subset if r in s]
+ return baseset([r for r in subset if r in s])
def first(repo, subset, x):
"""``first(set, [n])``
@@ -763,11 +761,11 @@
# include the revision responsible for the most recent version
s.add(cx.linkrev())
else:
- return []
+ return baseset([])
else:
s = set(_revancestors(repo, [c.rev()], followfirst)) | set([c.rev()])
- return [r for r in subset if r in s]
+ return baseset([r for r in subset if r in s])
def follow(repo, subset, x):
"""``follow([file])``
@@ -802,14 +800,15 @@
gr = re.compile(getstring(x, _("grep requires a string")))
except re.error, e:
raise error.ParseError(_('invalid match pattern: %s') % e)
- l = []
- for r in subset:
- c = repo[r]
+
+ def matches(x):
+ c = repo[x]
for e in c.files() + [c.user(), c.description()]:
if gr.search(e):
- l.append(r)
- break
- return l
+ return True
+ return False
+
+ return lazyset(subset, matches)
def _matchfiles(repo, subset, x):
# _matchfiles takes a revset list of prefixed arguments:
@@ -858,10 +857,10 @@
hasset = True
if not default:
default = 'glob'
- m = None
- s = []
- for r in subset:
- c = repo[r]
+
+ def matches(x):
+ m = None
+ c = repo[x]
if not m or (hasset and rev is None):
ctx = c
if rev is not None:
@@ -870,9 +869,10 @@
exclude=exc, ctx=ctx, default=default)
for f in c.files():
if m(f):
- s.append(r)
- break
- return s
+ return True
+ return False
+
+ return lazyset(subset, matches)
def hasfile(repo, subset, x):
"""``file(pattern)``
@@ -896,15 +896,15 @@
hs = set()
for b, ls in repo.branchmap().iteritems():
hs.update(repo[h].rev() for h in ls)
- return [r for r in subset if r in hs]
+ return baseset([r for r in subset if r in hs])
def heads(repo, subset, x):
"""``heads(set)``
Members of set with no children in set.
"""
s = getset(repo, subset, x)
- ps = set(parents(repo, subset, x))
- return [r for r in s if r not in ps]
+ ps = parents(repo, subset, x)
+ return s - ps
def hidden(repo, subset, x):
"""``hidden()``
@@ -913,7 +913,7 @@
# i18n: "hidden" is a keyword
getargs(x, 0, 0, _("hidden takes no arguments"))
hiddenrevs = repoview.filterrevs(repo, 'visible')
- return [r for r in subset if r in hiddenrevs]
+ return subset & hiddenrevs
def keyword(repo, subset, x):
"""``keyword(string)``
@@ -922,13 +922,13 @@
"""
# i18n: "keyword" is a keyword
kw = encoding.lower(getstring(x, _("keyword requires a string")))
- l = []
- for r in subset:
+
+ def matches(r):
c = repo[r]
- if util.any(kw in encoding.lower(t)
- for t in c.files() + [c.user(), c.description()]):
- l.append(r)
- return l
+ return util.any(kw in encoding.lower(t) for t in c.files() + [c.user(),
+ c.description()])
+
+ return lazyset(subset, matches)
def limit(repo, subset, x):
"""``limit(set, [n])``
@@ -944,9 +944,18 @@
except (TypeError, ValueError):
# i18n: "limit" is a keyword
raise error.ParseError(_("limit expects a number"))
- ss = set(subset)
- os = getset(repo, list(repo), l[0])[:lim]
- return [r for r in os if r in ss]
+ ss = subset.set()
+ os = getset(repo, spanset(repo), l[0])
+ bs = baseset([])
+ it = iter(os)
+ for x in xrange(lim):
+ try:
+ y = it.next()
+ if y in ss:
+ bs.append(y)
+ except (StopIteration):
+ break
+ return bs
def last(repo, subset, x):
"""``last(set, [n])``
@@ -962,20 +971,30 @@
except (TypeError, ValueError):
# i18n: "last" is a keyword
raise error.ParseError(_("last expects a number"))
- ss = set(subset)
- os = getset(repo, list(repo), l[0])[-lim:]
- return [r for r in os if r in ss]
+ ss = subset.set()
+ os = getset(repo, spanset(repo), l[0])
+ os.reverse()
+ bs = baseset([])
+ it = iter(os)
+ for x in xrange(lim):
+ try:
+ y = it.next()
+ if y in ss:
+ bs.append(y)
+ except (StopIteration):
+ break
+ return bs
def maxrev(repo, subset, x):
"""``max(set)``
Changeset with highest revision number in set.
"""
- os = getset(repo, list(repo), x)
+ os = getset(repo, spanset(repo), x)
if os:
m = max(os)
if m in subset:
- return [m]
- return []
+ return baseset([m])
+ return baseset([])
def merge(repo, subset, x):
"""``merge()``
@@ -984,7 +1003,7 @@
# i18n: "merge" is a keyword
getargs(x, 0, 0, _("merge takes no arguments"))
cl = repo.changelog
- return [r for r in subset if cl.parentrevs(r)[1] != -1]
+ return lazyset(subset, lambda r: cl.parentrevs(r)[1] != -1)
def branchpoint(repo, subset, x):
"""``branchpoint()``
@@ -994,25 +1013,35 @@
getargs(x, 0, 0, _("branchpoint takes no arguments"))
cl = repo.changelog
if not subset:
- return []
+ return baseset([])
baserev = min(subset)
parentscount = [0]*(len(repo) - baserev)
for r in cl.revs(start=baserev + 1):
for p in cl.parentrevs(r):
if p >= baserev:
parentscount[p - baserev] += 1
- return [r for r in subset if (parentscount[r - baserev] > 1)]
+ return baseset([r for r in subset if (parentscount[r - baserev] > 1)])
def minrev(repo, subset, x):
"""``min(set)``
Changeset with lowest revision number in set.
"""
- os = getset(repo, list(repo), x)
+ os = getset(repo, spanset(repo), x)
if os:
m = min(os)
if m in subset:
- return [m]
- return []
+ return baseset([m])
+ return baseset([])
+
+def _missingancestors(repo, subset, x):
+ # i18n: "_missingancestors" is a keyword
+ revs, bases = getargs(x, 2, 2,
+ _("_missingancestors requires two arguments"))
+ rs = baseset(repo)
+ revs = getset(repo, rs, revs)
+ bases = getset(repo, rs, bases)
+ missing = set(repo.changelog.findmissingrevs(bases, revs))
+ return baseset([r for r in subset if r in missing])
def modifies(repo, subset, x):
"""``modifies(pattern)``
@@ -1042,7 +1071,7 @@
if pm is not None:
rn = repo.changelog.rev(pm)
- return [r for r in subset if r == rn]
+ return baseset([r for r in subset if r == rn])
def obsolete(repo, subset, x):
"""``obsolete()``
@@ -1050,7 +1079,7 @@
# i18n: "obsolete" is a keyword
getargs(x, 0, 0, _("obsolete takes no arguments"))
obsoletes = obsmod.getrevs(repo, 'obsolete')
- return [r for r in subset if r in obsoletes]
+ return subset & obsoletes
def origin(repo, subset, x):
"""``origin([set])``
@@ -1061,9 +1090,9 @@
for the first operation is selected.
"""
if x is not None:
- args = set(getset(repo, list(repo), x))
+ args = getset(repo, spanset(repo), x).set()
else:
- args = set(getall(repo, list(repo), x))
+ args = getall(repo, spanset(repo), x).set()
def _firstsrc(rev):
src = _getrevsource(repo, rev)
@@ -1078,7 +1107,7 @@
src = prev
o = set([_firstsrc(r) for r in args])
- return [r for r in subset if r in o]
+ return baseset([r for r in subset if r in o])
def outgoing(repo, subset, x):
"""``outgoing([path])``
@@ -1101,7 +1130,7 @@
repo.ui.popbuffer()
cl = repo.changelog
o = set([cl.rev(r) for r in outgoing.missing])
- return [r for r in subset if r in o]
+ return baseset([r for r in subset if r in o])
def p1(repo, subset, x):
"""``p1([set])``
@@ -1109,13 +1138,13 @@
"""
if x is None:
p = repo[x].p1().rev()
- return [r for r in subset if r == p]
+ return baseset([r for r in subset if r == p])
ps = set()
cl = repo.changelog
- for r in getset(repo, list(repo), x):
+ for r in getset(repo, spanset(repo), x):
ps.add(cl.parentrevs(r)[0])
- return [r for r in subset if r in ps]
+ return subset & ps
def p2(repo, subset, x):
"""``p2([set])``
@@ -1125,15 +1154,15 @@
ps = repo[x].parents()
try:
p = ps[1].rev()
- return [r for r in subset if r == p]
+ return baseset([r for r in subset if r == p])
except IndexError:
- return []
+ return baseset([])
ps = set()
cl = repo.changelog
- for r in getset(repo, list(repo), x):
+ for r in getset(repo, spanset(repo), x):
ps.add(cl.parentrevs(r)[1])
- return [r for r in subset if r in ps]
+ return subset & ps
def parents(repo, subset, x):
"""``parents([set])``
@@ -1141,13 +1170,13 @@
"""
if x is None:
ps = tuple(p.rev() for p in repo[x].parents())
- return [r for r in subset if r in ps]
+ return subset & ps
ps = set()
cl = repo.changelog
- for r in getset(repo, list(repo), x):
+ for r in getset(repo, spanset(repo), x):
ps.update(cl.parentrevs(r))
- return [r for r in subset if r in ps]
+ return subset & ps
def parentspec(repo, subset, x, n):
"""``set^0``
@@ -1163,7 +1192,7 @@
raise error.ParseError(_("^ expects a number 0, 1, or 2"))
ps = set()
cl = repo.changelog
- for r in getset(repo, cl, x):
+ for r in getset(repo, baseset(cl), x):
if n == 0:
ps.add(r)
elif n == 1:
@@ -1172,7 +1201,7 @@
parents = cl.parentrevs(r)
if len(parents) > 1:
ps.add(parents[1])
- return [r for r in subset if r in ps]
+ return subset & ps
def present(repo, subset, x):
"""``present(set)``
@@ -1186,7 +1215,7 @@
try:
return getset(repo, subset, x)
except error.RepoLookupError:
- return []
+ return baseset([])
def public(repo, subset, x):
"""``public()``
@@ -1194,7 +1223,7 @@
# i18n: "public" is a keyword
getargs(x, 0, 0, _("public takes no arguments"))
pc = repo._phasecache
- return [r for r in subset if pc.phase(repo, r) == phases.public]
+ return lazyset(subset, lambda r: pc.phase(repo, r) == phases.public)
def remote(repo, subset, x):
"""``remote([id [,path]])``
@@ -1228,8 +1257,8 @@
if n in repo:
r = repo[n].rev()
if r in subset:
- return [r]
- return []
+ return baseset([r])
+ return baseset([])
def removes(repo, subset, x):
"""``removes(pattern)``
@@ -1255,7 +1284,7 @@
except (TypeError, ValueError):
# i18n: "rev" is a keyword
raise error.ParseError(_("rev expects a number"))
- return [r for r in subset if r == l]
+ return baseset([r for r in subset if r == l])
def matching(repo, subset, x):
"""``matching(revision [, field])``
@@ -1285,7 +1314,7 @@
# i18n: "matching" is a keyword
l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
- revs = getset(repo, repo.changelog, l[0])
+ revs = getset(repo, baseset(repo.changelog), l[0])
fieldlist = ['metadata']
if len(l) > 1:
@@ -1356,26 +1385,24 @@
# is only one field to match)
getinfo = lambda r: [f(r) for f in getfieldfuncs]
- matches = set()
- for rev in revs:
- target = getinfo(rev)
- for r in subset:
+ def matches(x):
+ for rev in revs:
+ target = getinfo(rev)
match = True
for n, f in enumerate(getfieldfuncs):
- if target[n] != f(r):
+ if target[n] != f(x):
match = False
- break
if match:
- matches.add(r)
- return [r for r in subset if r in matches]
+ return True
+ return False
+
+ return lazyset(subset, matches)
def reverse(repo, subset, x):
"""``reverse(set)``
Reverse order of set.
"""
l = getset(repo, subset, x)
- if not isinstance(l, list):
- l = list(l)
l.reverse()
return l
@@ -1383,10 +1410,10 @@
"""``roots(set)``
Changesets in set with no parent changeset in set.
"""
- s = set(getset(repo, repo.changelog, x))
- subset = [r for r in subset if r in s]
+ s = getset(repo, baseset(repo.changelog), x).set()
+ subset = baseset([r for r in subset if r in s])
cs = _children(repo, subset, s)
- return [r for r in subset if r not in cs]
+ return subset - cs
def secret(repo, subset, x):
"""``secret()``
@@ -1394,7 +1421,7 @@
# i18n: "secret" is a keyword
getargs(x, 0, 0, _("secret takes no arguments"))
pc = repo._phasecache
- return [r for r in subset if pc.phase(repo, r) == phases.secret]
+ return lazyset(subset, lambda x: pc.phase(repo, x) == phases.secret)
def sort(repo, subset, x):
"""``sort(set[, [-]key...])``
@@ -1450,7 +1477,7 @@
e.append(r)
l.append(e)
l.sort()
- return [e[-1] for e in l]
+ return baseset([e[-1] for e in l])
def _stringmatcher(pattern):
"""
@@ -1519,7 +1546,7 @@
s = set([cl.rev(n) for t, n in repo.tagslist() if matcher(t)])
else:
s = set([cl.rev(n) for t, n in repo.tagslist() if t != 'tip'])
- return [r for r in subset if r in s]
+ return subset & s
def tagged(repo, subset, x):
return tag(repo, subset, x)
@@ -1531,7 +1558,7 @@
# i18n: "unstable" is a keyword
getargs(x, 0, 0, _("unstable takes no arguments"))
unstables = obsmod.getrevs(repo, 'unstable')
- return [r for r in subset if r in unstables]
+ return subset & unstables
def user(repo, subset, x):
@@ -1548,11 +1575,29 @@
def _list(repo, subset, x):
s = getstring(x, "internal error")
if not s:
- return []
- if not isinstance(subset, set):
- subset = set(subset)
+ return baseset([])
ls = [repo[r].rev() for r in s.split('\0')]
- return [r for r in ls if r in subset]
+ s = subset.set()
+ return baseset([r for r in ls if r in s])
+
+# for internal use
+def _intlist(repo, subset, x):
+ s = getstring(x, "internal error")
+ if not s:
+ return baseset([])
+ ls = [int(r) for r in s.split('\0')]
+ s = subset.set()
+ return baseset([r for r in ls if r in s])
+
+# for internal use
+def _hexlist(repo, subset, x):
+ s = getstring(x, "internal error")
+ if not s:
+ return baseset([])
+ cl = repo.changelog
+ ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
+ s = subset.set()
+ return baseset([r for r in ls if r in s])
symbols = {
"adds": adds,
@@ -1598,6 +1643,7 @@
"max": maxrev,
"merge": merge,
"min": minrev,
+ "_missingancestors": _missingancestors,
"modifies": modifies,
"obsolete": obsolete,
"origin": origin,
@@ -1620,6 +1666,8 @@
"user": user,
"unstable": unstable,
"_list": _list,
+ "_intlist": _intlist,
+ "_hexlist": _hexlist,
}
# symbols which can't be used for a DoS attack for any given input
@@ -1667,6 +1715,7 @@
"max",
"merge",
"min",
+ "_missingancestors",
"modifies",
"obsolete",
"origin",
@@ -1689,6 +1738,8 @@
"user",
"unstable",
"_list",
+ "_intlist",
+ "_hexlist",
])
methods = {
@@ -1733,7 +1784,24 @@
elif op == 'and':
wa, ta = optimize(x[1], True)
wb, tb = optimize(x[2], True)
+
+ # (::x and not ::y)/(not ::y and ::x) have a fast path
+ def ismissingancestors(revs, bases):
+ return (
+ revs[0] == 'func'
+ and getstring(revs[1], _('not a symbol')) == 'ancestors'
+ and bases[0] == 'not'
+ and bases[1][0] == 'func'
+ and getstring(bases[1][1], _('not a symbol')) == 'ancestors')
+
w = min(wa, wb)
+ if ismissingancestors(ta, tb):
+ return w, ('func', ('symbol', '_missingancestors'),
+ ('list', ta[2], tb[1][2]))
+ if ismissingancestors(tb, ta):
+ return w, ('func', ('symbol', '_missingancestors'),
+ ('list', tb[2], ta[1][2]))
+
if wa > wb:
return w, (op, tb, ta)
return w, (op, ta, tb)
@@ -1917,7 +1985,9 @@
tree = findaliases(ui, tree)
weight, tree = optimize(tree, True)
def mfunc(repo, subset):
- return getset(repo, subset, tree)
+ if util.safehasattr(subset, 'set'):
+ return getset(repo, subset, tree)
+ return getset(repo, baseset(subset), tree)
return mfunc
def formatspec(expr, *args):
@@ -1976,11 +2046,11 @@
elif l == 1:
return argtype(t, s[0])
elif t == 'd':
- return "_list('%s')" % "\0".join(str(int(a)) for a in s)
+ return "_intlist('%s')" % "\0".join(str(int(a)) for a in s)
elif t == 's':
return "_list('%s')" % "\0".join(s)
elif t == 'n':
- return "_list('%s')" % "\0".join(node.hex(a) for a in s)
+ return "_hexlist('%s')" % "\0".join(node.hex(a) for a in s)
elif t == 'b':
return "_list('%s')" % "\0".join(a.branch() for a in s)
@@ -2046,5 +2116,238 @@
funcs.add(tree[1][1])
return funcs
+class baseset(list):
+ """Basic data structure that represents a revset and contains the basic
+ operation that it should be able to perform.
+ """
+ def __init__(self, data):
+ super(baseset, self).__init__(data)
+ self._set = None
+
+ def set(self):
+ if not self._set:
+ self._set = set(self)
+ return self._set
+
+ def __sub__(self, x):
+ if isinstance(x, baseset):
+ s = x.set()
+ else:
+ s = set(x)
+ return baseset(self.set() - s)
+
+ def __and__(self, x):
+ if isinstance(x, baseset):
+ x = x.set()
+ return baseset([y for y in self if y in x])
+
+ def __add__(self, x):
+ s = self.set()
+ l = [r for r in x if r not in s]
+ return baseset(list(self) + l)
+
+class lazyset(object):
+ """Duck type for baseset class which iterates lazily over the revisions in
+ the subset and contains a function which tests for membership in the
+ revset
+ """
+ def __init__(self, subset, condition=lambda x: True):
+ self._subset = subset
+ self._condition = condition
+ self._cache = {}
+
+ def __contains__(self, x):
+ c = self._cache
+ if x not in c:
+ c[x] = x in self._subset and self._condition(x)
+ return c[x]
+
+ def __iter__(self):
+ cond = self._condition
+ for x in self._subset:
+ if cond(x):
+ yield x
+
+ def __and__(self, x):
+ return lazyset(self, lambda r: r in x)
+
+ def __sub__(self, x):
+ return lazyset(self, lambda r: r not in x)
+
+ def __add__(self, x):
+ def iterates():
+ for r in self:
+ yield r
+ for r in x:
+ if r not in self:
+ yield r
+
+ return lazyset(generatorset(iterates()))
+
+ def __nonzero__(self):
+ for r in self:
+ return True
+ return False
+
+ def __len__(self):
+ # Basic implementation to be changed in future patches.
+ l = baseset([r for r in self])
+ return len(l)
+
+ def __getitem__(self, x):
+ # Basic implementation to be changed in future patches.
+ l = baseset([r for r in self])
+ return l[x]
+
+ def sort(self, reverse=False):
+ # Basic implementation to be changed in future patches.
+ self._subset = baseset(self._subset)
+ self._subset.sort(reverse=reverse)
+
+ def reverse(self):
+ self._subset.reverse()
+
+ def set(self):
+ return set([r for r in self])
+
+class generatorset(object):
+ """Wrapper structure for generators that provides lazy membership and can
+ be iterated more than once.
+ When asked for membership it generates values until either it finds the
+ requested one or has gone through all the elements in the generator
+ """
+ def __init__(self, gen):
+ self._gen = gen
+ self._iter = iter(gen)
+ self._cache = {}
+ self._genlist = baseset([])
+ self._iterated = False
+
+ def _nextitem(self):
+ l = self._iter.next()
+ self._cache[l] = True
+ self._genlist.append(l)
+ return l
+
+ def __contains__(self, x):
+ if x in self._cache:
+ return self._cache[x]
+
+ self._iterated = True
+ while True:
+ try:
+ l = self._nextitem()
+ if l == x:
+ return True
+ except (StopIteration):
+ break
+
+ self._cache[x] = False
+ return False
+
+ def __iter__(self):
+ if self._iterated:
+ for l in self._genlist:
+ yield l
+ while True:
+ try:
+ item = self._nextitem()
+ yield item
+ except (StopIteration):
+ break
+ else:
+ self._iterated = True
+ for item in self._gen:
+ self._cache[item] = True
+ self._genlist.append(item)
+ yield item
+
+ def set(self):
+ return self
+
+class spanset(object):
+ """Duck type for baseset class which represents a range of revisions and
+ can work lazily and without having all the range in memory
+ """
+ def __init__(self, repo, start=0, end=None):
+ self._start = start
+ if end is not None:
+ self._end = end
+ else:
+ self._end = len(repo)
+ self._hiddenrevs = repo.changelog.filteredrevs
+
+ def _contained(self, rev):
+ return (rev <= self._start and rev > self._end) or (rev >= self._start
+ and rev < self._end)
+
+ def __iter__(self):
+ if self._start <= self._end:
+ iterrange = xrange(self._start, self._end)
+ else:
+ iterrange = xrange(self._start, self._end, -1)
+
+ if self._hiddenrevs:
+ s = self._hiddenrevs
+ for r in iterrange:
+ if r not in s:
+ yield r
+ else:
+ for r in iterrange:
+ yield r
+
+ def __contains__(self, x):
+ return self._contained(x) and not (self._hiddenrevs and rev in
+ self._hiddenrevs)
+
+ def __and__(self, x):
+ if isinstance(x, baseset):
+ x = x.set()
+ return lazyset(self, lambda r: r in x)
+
+ def __sub__(self, x):
+ if isinstance(x, baseset):
+ x = x.set()
+ return lazyset(self, lambda r: r not in x)
+
+ def __add__(self, x):
+ def iterates():
+ for r in self:
+ yield r
+ for r in x:
+ if r not in self:
+ yield r
+
+ return lazyset(generatorset(iterates()))
+
+ def __len__(self):
+ if not self._hiddenrevs:
+ return abs(self._end - self._start)
+ else:
+ count = 0
+ for rev in self._hiddenrevs:
+ if self._contained(rev):
+ count += 1
+ return abs(self._end - self._start) - count
+
+ def __getitem__(self, x):
+ # Basic implementation to be changed in future patches.
+ l = baseset([r for r in self])
+ return l[x]
+
+ def sort(self, reverse=False):
+ # Basic implementation to be changed in future patches.
+ if reverse:
+ self.reverse()
+
+ def reverse(self):
+ if self._start <= self._end:
+ self._start, self._end = self._end - 1, self._start - 1
+ else:
+ self._start, self._end = self._end + 1, self._start + 1
+
+ def set(self):
+ return self
+
# tell hggettext to extract docstrings from these functions:
i18nfunctions = symbols.values()
--- a/mercurial/scmutil.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/scmutil.py Thu Feb 27 18:57:03 2014 -0600
@@ -20,6 +20,16 @@
systemrcpath = scmplatform.systemrcpath
userrcpath = scmplatform.userrcpath
+def itersubrepos(ctx1, ctx2):
+ """find subrepos in ctx1 or ctx2"""
+ # Create a (subpath, ctx) mapping where we prefer subpaths from
+ # ctx1. The subpaths from ctx2 are important when the .hgsub file
+ # has been modified (in ctx2) but not yet committed (in ctx1).
+ subpaths = dict.fromkeys(ctx2.substate, ctx2)
+ subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
+ for subpath, ctx in sorted(subpaths.iteritems()):
+ yield subpath, ctx.sub(subpath)
+
def nochangesfound(ui, repo, excluded=None):
'''Report no changes for push/pull, excluded is None or a list of
nodes excluded from the push/pull.
@@ -480,7 +490,7 @@
return defval
return repo[val].rev()
- seen, l = set(), []
+ seen, l = set(), revset.baseset([])
for spec in revs:
if l and not seen:
seen = set(l)
@@ -489,7 +499,7 @@
try:
if isinstance(spec, int):
seen.add(spec)
- l.append(spec)
+ l = l + [spec]
continue
if _revrangesep in spec:
@@ -501,7 +511,7 @@
rangeiter = repo.changelog.revs(start, end)
if not seen and not l:
# by far the most common case: revs = ["-1:0"]
- l = list(rangeiter)
+ l = revset.baseset(rangeiter)
# defer syncing seen until next iteration
continue
newrevs = set(rangeiter)
@@ -510,23 +520,26 @@
seen.update(newrevs)
else:
seen = newrevs
- l.extend(sorted(newrevs, reverse=start > end))
+ l = l + sorted(newrevs, reverse=start > end)
continue
elif spec and spec in repo: # single unquoted rev
rev = revfix(repo, spec, None)
if rev in seen:
continue
seen.add(rev)
- l.append(rev)
+ l = l + [rev]
continue
except error.RepoLookupError:
pass
# fall through to new-style queries if old-style fails
m = revset.match(repo.ui, spec)
- dl = [r for r in m(repo, list(repo)) if r not in seen]
- l.extend(dl)
- seen.update(dl)
+ if seen or l:
+ dl = [r for r in m(repo, revset.spanset(repo)) if r not in seen]
+ l = l + dl
+ seen.update(dl)
+ else:
+ l = m(repo, revset.spanset(repo))
return l
--- a/mercurial/subrepo.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/subrepo.py Thu Feb 27 18:57:03 2014 -0600
@@ -326,16 +326,6 @@
os.unlink(os.path.join(dirname, f))
os.walk(path, v, None)
-def itersubrepos(ctx1, ctx2):
- """find subrepos in ctx1 or ctx2"""
- # Create a (subpath, ctx) mapping where we prefer subpaths from
- # ctx1. The subpaths from ctx2 are important when the .hgsub file
- # has been modified (in ctx2) but not yet committed (in ctx1).
- subpaths = dict.fromkeys(ctx2.substate, ctx2)
- subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
- for subpath, ctx in sorted(subpaths.iteritems()):
- yield subpath, ctx.sub(subpath)
-
def subrepo(ctx, path):
"""return instance of the right subrepo class for subrepo in path"""
# subrepo inherently violates our import layering rules
--- a/mercurial/templatekw.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/templatekw.py Thu Feb 27 18:57:03 2014 -0600
@@ -195,8 +195,12 @@
""":bookmarks: List of strings. Any bookmarks associated with the
changeset.
"""
+ repo = args['ctx']._repo
bookmarks = args['ctx'].bookmarks()
- return showlist('bookmark', bookmarks, **args)
+ hybrid = showlist('bookmark', bookmarks, **args)
+ for value in hybrid.values:
+ value['current'] = repo._bookmarkcurrent
+ return hybrid
def showchildren(**args):
""":children: List of strings. The children of the changeset."""
--- a/mercurial/templater.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/templater.py Thu Feb 27 18:57:03 2014 -0600
@@ -7,7 +7,7 @@
from i18n import _
import sys, os, re
-import util, config, templatefilters, parser, error
+import util, config, templatefilters, templatekw, parser, error
import types
import minirst
@@ -245,6 +245,31 @@
return templatefilters.fill(text, width, initindent, hangindent)
+def pad(context, mapping, args):
+ """usage: pad(text, width, fillchar=' ', right=False)
+ """
+ if not (2 <= len(args) <= 4):
+ raise error.ParseError(_("pad() expects two to four arguments"))
+
+ width = int(args[1][1])
+
+ text = stringify(args[0][0](context, mapping, args[0][1]))
+ if args[0][0] == runstring:
+ text = stringify(runtemplate(context, mapping,
+ compiletemplate(text, context)))
+
+ right = False
+ fillchar = ' '
+ if len(args) > 2:
+ fillchar = stringify(args[2][0](context, mapping, args[2][1]))
+ if len(args) > 3:
+ right = util.parsebool(args[3][1])
+
+ if right:
+ return text.rjust(width, fillchar)
+ else:
+ return text.ljust(width, fillchar)
+
def get(context, mapping, args):
if len(args) != 2:
# i18n: "get" is a keyword
@@ -276,6 +301,19 @@
elif len(args) == 3:
yield _evalifliteral(args[2], context, mapping)
+def ifcontains(context, mapping, args):
+ if not (3 <= len(args) <= 4):
+ # i18n: "ifcontains" is a keyword
+ raise error.ParseError(_("ifcontains expects three or four arguments"))
+
+ item = stringify(args[0][0](context, mapping, args[0][1]))
+ items = args[1][0](context, mapping, args[1][1])
+
+ if item in items:
+ yield _evalifliteral(args[2], context, mapping)
+ elif len(args) == 4:
+ yield _evalifliteral(args[3], context, mapping)
+
def ifeq(context, mapping, args):
if not (3 <= len(args) <= 4):
# i18n: "ifeq" is a keyword
@@ -318,6 +356,32 @@
# ignore args[0] (the label string) since this is supposed to be a a no-op
yield _evalifliteral(args[1], context, mapping)
+def revset(context, mapping, args):
+ """usage: revset(query[, formatargs...])
+ """
+ if not len(args) > 0:
+ # i18n: "revset" is a keyword
+ raise error.ParseError(_("revset expects one or more arguments"))
+
+ raw = args[0][1]
+ ctx = mapping['ctx']
+ repo = ctx._repo
+
+ if len(args) > 1:
+ formatargs = list([a[0](context, mapping, a[1]) for a in args[1:]])
+ revs = repo.revs(raw, *formatargs)
+ revs = list([str(r) for r in revs])
+ else:
+ revsetcache = mapping['cache'].setdefault("revsetcache", {})
+ if raw in revsetcache:
+ revs = revsetcache[raw]
+ else:
+ revs = repo.revs(raw)
+ revs = list([str(r) for r in revs])
+ revsetcache[raw] = revs
+
+ return templatekw.showlist("revision", revs, **mapping)
+
def rstdoc(context, mapping, args):
if len(args) != 2:
# i18n: "rstdoc" is a keyword
@@ -328,6 +392,57 @@
return minirst.format(text, style=style, keep=['verbose'])
+def shortest(context, mapping, args):
+ """usage: shortest(node, minlength=4)
+ """
+ if not (1 <= len(args) <= 2):
+ raise error.ParseError(_("shortest() expects one or two arguments"))
+
+ node = stringify(args[0][0](context, mapping, args[0][1]))
+
+ minlength = 4
+ if len(args) > 1:
+ minlength = int(args[1][1])
+
+ cl = mapping['ctx']._repo.changelog
+ def isvalid(test):
+ try:
+ try:
+ cl.index.partialmatch(test)
+ except AttributeError:
+ # Pure mercurial doesn't support partialmatch on the index.
+ # Fallback to the slow way.
+ if cl._partialmatch(test) is None:
+ return False
+
+ try:
+ i = int(test)
+ # if we are a pure int, then starting with zero will not be
+ # confused as a rev; or, obviously, if the int is larger than
+ # the value of the tip rev
+ if test[0] == '0' or i > len(cl):
+ return True
+ return False
+ except ValueError:
+ return True
+ except error.RevlogError:
+ return False
+
+ shortest = node
+ startlength = max(6, minlength)
+ length = startlength
+ while True:
+ test = node[:length]
+ if isvalid(test):
+ shortest = test
+ if length == minlength or length > startlength:
+ return shortest
+ length -= 1
+ else:
+ length += 1
+ if len(shortest) <= length:
+ return shortest
+
def strip(context, mapping, args):
if not (1 <= len(args) <= 2):
raise error.ParseError(_("strip expects one or two arguments"))
@@ -365,10 +480,14 @@
"fill": fill,
"get": get,
"if": if_,
+ "ifcontains": ifcontains,
"ifeq": ifeq,
"join": join,
"label": label,
+ "pad": pad,
+ "revset": revset,
"rstdoc": rstdoc,
+ "shortest": shortest,
"strip": strip,
"sub": sub,
}
--- a/mercurial/transaction.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/transaction.py Thu Feb 27 18:57:03 2014 -0600
@@ -174,7 +174,10 @@
lines = fp.readlines()
fp.close()
for l in lines:
- f, o = l.split('\0')
- entries.append((f, int(o), None))
+ try:
+ f, o = l.split('\0')
+ entries.append((f, int(o), None))
+ except ValueError:
+ report(_("couldn't read journal entry %r!\n") % l)
_playback(file, report, opener, entries)
--- a/mercurial/ui.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/ui.py Thu Feb 27 18:57:03 2014 -0600
@@ -449,7 +449,9 @@
except KeyError:
pass
if not user:
- raise util.Abort(_('no username supplied (see "hg help config")'))
+ raise util.Abort(_('no username supplied'),
+ hint=_('use "hg config --edit" '
+ 'to set your username'))
if "\n" in user:
raise util.Abort(_("username %s contains a newline\n") % repr(user))
return user
--- a/mercurial/util.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/util.py Thu Feb 27 18:57:03 2014 -0600
@@ -1989,12 +1989,14 @@
for source, hook in self._hooks:
hook(*args)
-def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr):
+def debugstacktrace(msg='stacktrace', skip=0, f=sys.stderr, otherf=sys.stdout):
'''Writes a message to f (stderr) with a nicely formatted stacktrace.
- Skips the 'skip' last entries.
+ Skips the 'skip' last entries. By default it will flush stdout first.
It can be used everywhere and do intentionally not require an ui object.
Not be used in production code but very convenient while developing.
'''
+ if otherf:
+ otherf.flush()
f.write('%s at:\n' % msg)
entries = [('%s:%s' % (fn, ln), func)
for fn, ln, func, _text in traceback.extract_stack()[:-skip - 1]]
@@ -2002,6 +2004,7 @@
fnmax = max(len(entry[0]) for entry in entries)
for fnln, func in entries:
f.write(' %-*s in %s\n' % (fnmax, fnln, func))
+ f.flush()
# convenient shortcut
dst = debugstacktrace
--- a/mercurial/win32.py Tue Feb 25 18:45:01 2014 -0800
+++ b/mercurial/win32.py Thu Feb 27 18:57:03 2014 -0600
@@ -24,6 +24,7 @@
# GetLastError
_ERROR_SUCCESS = 0
+_ERROR_SHARING_VIOLATION = 32
_ERROR_INVALID_PARAMETER = 87
_ERROR_INSUFFICIENT_BUFFER = 122
@@ -59,7 +60,9 @@
_OPEN_EXISTING = 3
+_FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
_FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
+_FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
# SetFileAttributes
_FILE_ATTRIBUTE_NORMAL = 0x80
@@ -119,6 +122,27 @@
_STD_ERROR_HANDLE = _DWORD(-12).value
+# CreateToolhelp32Snapshot, Process32First, Process32Next
+_TH32CS_SNAPPROCESS = 0x00000002
+_MAX_PATH = 260
+
+class _tagPROCESSENTRY32(ctypes.Structure):
+ _fields_ = [('dwsize', _DWORD),
+ ('cntUsage', _DWORD),
+ ('th32ProcessID', _DWORD),
+ ('th32DefaultHeapID', ctypes.c_void_p),
+ ('th32ModuleID', _DWORD),
+ ('cntThreads', _DWORD),
+ ('th32ParentProcessID', _DWORD),
+ ('pcPriClassBase', _LONG),
+ ('dwFlags', _DWORD),
+ ('szExeFile', ctypes.c_char * _MAX_PATH)]
+
+ def __init__(self):
+ super(_tagPROCESSENTRY32, self).__init__()
+ self.dwsize = ctypes.sizeof(self)
+
+
# types of parameters of C functions used (required by pypy)
_kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
@@ -186,6 +210,15 @@
_user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
_user32.EnumWindows.restype = _BOOL
+_kernel32.CreateToolhelp32Snapshot.argtypes = [_DWORD, _DWORD]
+_kernel32.CreateToolhelp32Snapshot.restype = _BOOL
+
+_kernel32.Process32First.argtypes = [_HANDLE, ctypes.c_void_p]
+_kernel32.Process32First.restype = _BOOL
+
+_kernel32.Process32Next.argtypes = [_HANDLE, ctypes.c_void_p]
+_kernel32.Process32Next.restype = _BOOL
+
def _raiseoserror(name):
err = ctypes.WinError()
raise OSError(err.errno, '%s: %s' % (name, err.strerror))
@@ -309,6 +342,51 @@
width = csbi.srWindow.Right - csbi.srWindow.Left
return width
+def _1stchild(pid):
+ '''return the 1st found child of the given pid
+
+ None is returned when no child is found'''
+ pe = _tagPROCESSENTRY32()
+
+ # create handle to list all processes
+ ph = _kernel32.CreateToolhelp32Snapshot(_TH32CS_SNAPPROCESS, 0)
+ if ph == _INVALID_HANDLE_VALUE:
+ raise ctypes.WinError
+ try:
+ r = _kernel32.Process32First(ph, ctypes.byref(pe))
+ # loop over all processes
+ while r:
+ if pe.th32ParentProcessID == pid:
+ # return first child found
+ return pe.th32ProcessID
+ r = _kernel32.Process32Next(ph, ctypes.byref(pe))
+ finally:
+ _kernel32.CloseHandle(ph)
+ if _kernel32.GetLastError() != _ERROR_NO_MORE_FILES:
+ raise ctypes.WinError
+ return None # no child found
+
+class _tochildpid(int): # pid is _DWORD, which always matches in an int
+ '''helper for spawndetached, returns the child pid on conversion to string
+
+ Does not resolve the child pid immediately because the child may not yet be
+ started.
+ '''
+ def childpid(self):
+ '''returns the child pid of the first found child of the process
+ with this pid'''
+ return _1stchild(self)
+ def __str__(self):
+ # run when the pid is written to the file
+ ppid = self.childpid()
+ if ppid is None:
+ # race, child has exited since check
+ # fall back to this pid. Its process will also have disappeared,
+ # raising the same error type later as when the child pid would
+ # be returned.
+ return " %d" % self
+ return str(ppid)
+
def spawndetached(args):
# No standard library function really spawns a fully detached
# process under win32 because they allocate pipes or other objects
@@ -339,16 +417,24 @@
if not res:
raise ctypes.WinError
- return pi.dwProcessId
+ # _tochildpid because the process is the child of COMSPEC
+ return _tochildpid(pi.dwProcessId)
def unlink(f):
'''try to implement POSIX' unlink semantics on Windows'''
- if os.path.isdir(f):
- # use EPERM because it is POSIX prescribed value, even though
- # unlink(2) on directories returns EISDIR on Linux
- raise IOError(errno.EPERM,
- "Unlinking directory not permitted: '%s'" % f)
+ # If we can open f exclusively, no other processes must have open handles
+ # for it and we can expect its name will be deleted immediately when we
+ # close the handle unless we have another in the same process. We also
+ # expect we shall simply fail to open f if it is a directory.
+ fh = _kernel32.CreateFileA(f, 0, 0, None, _OPEN_EXISTING,
+ _FILE_FLAG_OPEN_REPARSE_POINT | _FILE_FLAG_DELETE_ON_CLOSE, None)
+ if fh != _INVALID_HANDLE_VALUE:
+ _kernel32.CloseHandle(fh)
+ return
+ error = _kernel32.GetLastError()
+ if error != _ERROR_SHARING_VIOLATION:
+ raise ctypes.WinError(error)
# POSIX allows to unlink and rename open files. Windows has serious
# problems with doing that:
--- a/tests/hghave.py Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/hghave.py Thu Feb 27 18:57:03 2014 -0600
@@ -248,6 +248,9 @@
except ImportError:
return False
+def has_python243():
+ return sys.version_info >= (2, 4, 3)
+
def has_outer_repo():
# failing for other reasons than 'no repo' imply that there is a repo
return not matchoutput('hg root 2>&1',
@@ -320,6 +323,7 @@
"p4": (has_p4, "Perforce server and client"),
"pyflakes": (has_pyflakes, "Pyflakes python linter"),
"pygments": (has_pygments, "Pygments source highlighting library"),
+ "python243": (has_python243, "python >= 2.4.3"),
"root": (has_root, "root permissions"),
"serve": (has_serve, "platform and python can manage 'hg serve -d'"),
"ssl": (has_ssl, "python >= 2.6 ssl module and python OpenSSL"),
--- a/tests/killdaemons.py Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/killdaemons.py Thu Feb 27 18:57:03 2014 -0600
@@ -4,13 +4,51 @@
if os.name =='nt':
import ctypes
+
+ def _check(ret, expectederr=None):
+ if ret == 0:
+ winerrno = ctypes.GetLastError()
+ if winerrno == expectederr:
+ return True
+ raise ctypes.WinError(winerrno)
+
def kill(pid, logfn, tryhard=True):
logfn('# Killing daemon process %d' % pid)
PROCESS_TERMINATE = 1
+ PROCESS_QUERY_INFORMATION = 0x400
+ SYNCHRONIZE = 0x00100000L
+ WAIT_OBJECT_0 = 0
+ WAIT_TIMEOUT = 258
handle = ctypes.windll.kernel32.OpenProcess(
- PROCESS_TERMINATE, False, pid)
- ctypes.windll.kernel32.TerminateProcess(handle, -1)
- ctypes.windll.kernel32.CloseHandle(handle)
+ PROCESS_TERMINATE|SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
+ False, pid)
+ if handle == 0:
+ _check(0, 87) # err 87 when process not found
+ return # process not found, already finished
+ try:
+ r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100)
+ if r == WAIT_OBJECT_0:
+ pass # terminated, but process handle still available
+ elif r == WAIT_TIMEOUT:
+ _check(ctypes.windll.kernel32.TerminateProcess(handle, -1))
+ else:
+ _check(r)
+
+ # TODO?: forcefully kill when timeout
+ # and ?shorter waiting time? when tryhard==True
+ r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100)
+ # timeout = 100 ms
+ if r == WAIT_OBJECT_0:
+ pass # process is terminated
+ elif r == WAIT_TIMEOUT:
+ logfn('# Daemon process %d is stuck')
+ else:
+ check(r) # any error
+ except: #re-raises
+ ctypes.windll.kernel32.CloseHandle(handle) # no _check, keep error
+ raise
+ _check(ctypes.windll.kernel32.CloseHandle(handle))
+
else:
def kill(pid, logfn, tryhard=True):
try:
--- a/tests/run-tests.py Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/run-tests.py Thu Feb 27 18:57:03 2014 -0600
@@ -434,7 +434,7 @@
if getattr(os, 'symlink', None):
vlog("# Making python executable in test path a symlink to '%s'" %
sys.executable)
- mypython = os.path.join(BINDIR, pyexename)
+ mypython = os.path.join(TMPBINDIR, pyexename)
try:
if os.readlink(mypython) == sys.executable:
return
@@ -776,9 +776,9 @@
# Merge the script output back into a unified test
+ warnonly = True
pos = -1
postout = []
- ret = 0
for l in output:
lout, lcmd = l, None
if salt in l:
@@ -797,11 +797,10 @@
if isinstance(r, str):
if r == '+glob':
lout = el[:-1] + ' (glob)\n'
- r = False
+ r = 0 # warn only
elif r == '-glob':
- log('\ninfo, unnecessary glob in %s (after line %d):'
- ' %s (glob)\n' % (test, pos, el[-1]))
- r = True # pass on unnecessary glob
+ lout = ''.join(el.rsplit(' (glob)', 1))
+ r = 0 # warn only
else:
log('\ninfo, unknown linematch result: %r\n' % r)
r = False
@@ -811,6 +810,8 @@
if needescape(lout):
lout = stringescape(lout.rstrip('\n')) + " (esc)\n"
postout.append(" " + lout) # let diff deal with it
+ if r != 0: # != warn only
+ warnonly = False
if lcmd:
# add on last return code
@@ -825,6 +826,8 @@
if pos in after:
postout += after.pop(pos)
+ if warnonly and exitcode == 0:
+ exitcode = False
return exitcode, postout
wifexited = getattr(os, "WIFEXITED", lambda x: False)
@@ -882,8 +885,9 @@
return 's', test, msg
def fail(msg, ret):
+ warned = ret is False
if not options.nodiff:
- log("\nERROR: %s %s" % (testpath, msg))
+ log("\n%s: %s %s" % (warned and 'Warning' or 'ERROR', test, msg))
if (not ret and options.interactive
and os.path.exists(testpath + ".err")):
iolock.acquire()
@@ -896,7 +900,7 @@
else:
rename(testpath + ".err", testpath + ".out")
return '.', test, ''
- return '!', test, msg
+ return warned and '~' or '!', test, msg
def success():
return '.', test, ''
@@ -1075,7 +1079,7 @@
' (expected %s)\n'
% (verb, actualhg, expecthg))
-results = {'.':[], '!':[], 's':[], 'i':[]}
+results = {'.':[], '!':[], '~': [], 's':[], 'i':[]}
times = []
iolock = threading.Lock()
abort = False
@@ -1139,7 +1143,8 @@
scheduletests(options, tests)
failed = len(results['!'])
- tested = len(results['.']) + failed
+ warned = len(results['~'])
+ tested = len(results['.']) + failed + warned
skipped = len(results['s'])
ignored = len(results['i'])
@@ -1147,11 +1152,13 @@
if not options.noskips:
for s in results['s']:
print "Skipped %s: %s" % s
+ for s in results['~']:
+ print "Warned %s: %s" % s
for s in results['!']:
print "Failed %s: %s" % s
_checkhglib("Tested")
- print "# Ran %d tests, %d skipped, %d failed." % (
- tested, skipped + ignored, failed)
+ print "# Ran %d tests, %d skipped, %d warned, %d failed." % (
+ tested, skipped + ignored, warned, failed)
if results['!']:
print 'python hash seed:', os.environ['PYTHONHASHSEED']
if options.time:
@@ -1164,7 +1171,9 @@
print "\ninterrupted!"
if failed:
- sys.exit(1)
+ return 1
+ if warned:
+ return 80
testtypes = [('.py', pytest, '.out'),
('.t', tsttest, '')]
@@ -1206,7 +1215,7 @@
# we do the randomness ourself to know what seed is used
os.environ['PYTHONHASHSEED'] = str(random.getrandbits(32))
- global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
+ global TESTDIR, HGTMP, INST, BINDIR, TMPBINDIR, PYTHONDIR, COVERAGE_FILE
TESTDIR = os.environ["TESTDIR"] = os.getcwd()
if options.tmpdir:
options.keep_tmpdir = True
@@ -1235,6 +1244,8 @@
if options.with_hg:
INST = None
BINDIR = os.path.dirname(os.path.realpath(options.with_hg))
+ TMPBINDIR = os.path.join(HGTMP, 'install', 'bin')
+ os.makedirs(TMPBINDIR)
# This looks redundant with how Python initializes sys.path from
# the location of the script being executed. Needed because the
@@ -1245,18 +1256,22 @@
else:
INST = os.path.join(HGTMP, "install")
BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
+ TMPBINDIR = BINDIR
PYTHONDIR = os.path.join(INST, "lib", "python")
os.environ["BINDIR"] = BINDIR
os.environ["PYTHON"] = PYTHON
path = [BINDIR] + os.environ["PATH"].split(os.pathsep)
+ if TMPBINDIR != BINDIR:
+ path = [TMPBINDIR] + path
os.environ["PATH"] = os.pathsep.join(path)
# Include TESTDIR in PYTHONPATH so that out-of-tree extensions
# can run .../tests/run-tests.py test-foo where test-foo
- # adds an extension to HGRC
- pypath = [PYTHONDIR, TESTDIR]
+ # adds an extension to HGRC. Also include run-test.py directory to import
+ # modules like heredoctest.
+ pypath = [PYTHONDIR, TESTDIR, os.path.abspath(os.path.dirname(__file__))]
# We have to augment PYTHONPATH, rather than simply replacing
# it, in case external libraries are only available via current
# PYTHONPATH. (In particular, the Subversion bindings on OS X
@@ -1274,7 +1289,7 @@
vlog("# Using", IMPL_PATH, os.environ[IMPL_PATH])
try:
- runtests(options, tests)
+ sys.exit(runtests(options, tests) or 0)
finally:
time.sleep(.1)
cleanup(options)
--- a/tests/test-command-template.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-command-template.t Thu Feb 27 18:57:03 2014 -0600
@@ -1626,3 +1626,60 @@
$ hg log -r 0 --template '{if(branches, "yes", "no")}\n'
no
+
+Test shortest(node) function:
+
+ $ echo b > b
+ $ hg ci -qAm b
+ $ hg log --template '{shortest(node)}\n'
+ d97c
+ f776
+ $ hg log --template '{shortest(node, 10)}\n'
+ d97c383ae3
+ f7769ec2ab
+
+Test pad function
+
+ $ hg log --template '{pad(rev, 20)} {author|user}\n'
+ 1 test
+ 0 test
+
+ $ hg log --template '{pad(rev, 20, " ", True)} {author|user}\n'
+ 1 test
+ 0 test
+
+ $ hg log --template '{pad(rev, 20, "-", False)} {author|user}\n'
+ 1------------------- test
+ 0------------------- test
+
+Test ifcontains function
+
+ $ hg log --template '{rev} {ifcontains("a", file_adds, "added a", "did not add a")}\n'
+ 1 did not add a
+ 0 added a
+
+Test revset function
+
+ $ hg log --template '{rev} {ifcontains(rev, revset("."), "current rev", "not current rev")}\n'
+ 1 current rev
+ 0 not current rev
+
+ $ hg log --template '{rev} Parents: {revset("parents(%s)", rev)}\n'
+ 1 Parents: 0
+ 0 Parents:
+
+ $ hg log --template 'Rev: {rev}\n{revset("::%s", rev) % "Ancestor: {revision}\n"}\n'
+ Rev: 1
+ Ancestor: 0
+ Ancestor: 1
+
+ Rev: 0
+ Ancestor: 0
+
+Test current bookmark templating
+
+ $ hg book foo
+ $ hg book bar
+ $ hg log --template "{rev} {bookmarks % '{bookmark}{ifeq(bookmark, current, \"*\")} '}\n"
+ 1 bar* foo
+ 0
--- a/tests/test-commandserver.py Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-commandserver.py Thu Feb 27 18:57:03 2014 -0600
@@ -267,7 +267,10 @@
runcommand(server, ['up', 'null'])
runcommand(server, ['phase', '-df', 'tip'])
- os.system('hg debugobsolete `hg log -r tip --template {node}`')
+ cmd = 'hg debugobsolete `hg log -r tip --template {node}`'
+ if os.name == 'nt':
+ cmd = 'sh -c "%s"' % cmd # run in sh, not cmd.exe
+ os.system(cmd)
runcommand(server, ['log', '--hidden'])
runcommand(server, ['log'])
--- a/tests/test-committer.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-committer.t Thu Feb 27 18:57:03 2014 -0600
@@ -49,7 +49,8 @@
$ echo "[ui]" > .hg/hgrc
$ echo "username = " >> .hg/hgrc
$ hg commit -m commit-1
- abort: no username supplied (see "hg help config")
+ abort: no username supplied
+ (use "hg config --edit" to set your username)
[255]
$ rm .hg/hgrc
$ hg commit -m commit-1 2>&1
--- a/tests/test-completion.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-completion.t Thu Feb 27 18:57:03 2014 -0600
@@ -13,6 +13,7 @@
cat
clone
commit
+ config
copy
diff
export
@@ -43,7 +44,6 @@
rollback
root
serve
- showconfig
status
summary
tag
@@ -222,6 +222,7 @@
branches: active, closed
bundle: force, rev, branch, base, all, type, ssh, remotecmd, insecure
cat: output, rev, decode, include, exclude
+ config: untrusted, edit
copy: after, force, include, exclude, dry-run
debugancestor:
debugbuilddag: mergeable-file, overwritten-file, new-file
@@ -250,7 +251,7 @@
debugrebuilddirstate: rev
debugrename: rev
debugrevlog: changelog, manifest, dump
- debugrevspec:
+ debugrevspec: optimize
debugsetparents:
debugsub: rev
debugsuccessorssets:
@@ -275,7 +276,6 @@
revert: all, date, rev, no-backup, include, exclude, dry-run
rollback: dry-run, force
root:
- showconfig: untrusted
tag: force, local, rev, remove, edit, message, date, user
tags:
tip: patch, git, style, template
--- a/tests/test-convert-hg-sink.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-convert-hg-sink.t Thu Feb 27 18:57:03 2014 -0600
@@ -119,7 +119,7 @@
0 add baz
$ cd new-filemap
$ hg tags
- tip 2:6f4fd1df87fb
+ tip 2:3c74706b1ff8
some-tag 0:ba8636729451
$ cd ..
--- a/tests/test-convert-hg-svn.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-convert-hg-svn.t Thu Feb 27 18:57:03 2014 -0600
@@ -103,3 +103,14 @@
scanning source...
sorting...
converting...
+
+verify which shamap format we are storing and must be able to handle
+
+ $ cat svn-repo-hg/.hg/shamap
+ svn:????????-????-????-????-????????????@1 ???????????????????????????????????????? (glob)
+ svn:????????-????-????-????-????????????@2 ???????????????????????????????????????? (glob)
+ svn:????????-????-????-????-????????????@2 ???????????????????????????????????????? (glob)
+ $ cat svn-repo-wc/.svn/hg-shamap
+ ???????????????????????????????????????? 1 (glob)
+ ???????????????????????????????????????? svn:????????-????-????-????-????????????@2 (glob)
+ ???????????????????????????????????????? svn:????????-????-????-????-????????????@2 (glob)
--- a/tests/test-convert-svn-source.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-convert-svn-source.t Thu Feb 27 18:57:03 2014 -0600
@@ -198,11 +198,12 @@
extra: convert_revision=svn:........-....-....-....-............/proj B/mytrunk@1 (re)
$ cd ..
-Test converting empty heads (issue3347)
+Test converting empty heads (issue3347).
+Also tests getting logs directly without debugsvnlog.
$ svnadmin create svn-empty
$ svnadmin load -q svn-empty < "$TESTDIR/svn/empty.svndump"
- $ hg --config convert.svn.trunk= convert svn-empty
+ $ hg --config convert.svn.trunk= --config convert.svn.debugsvnlog=0 convert svn-empty
assuming destination svn-empty-hg
initializing destination svn-empty-hg repository
scanning source...
--- a/tests/test-convert.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-convert.t Thu Feb 27 18:57:03 2014 -0600
@@ -121,6 +121,14 @@
can be used to (for instance) move code in one repository from "default"
to a named branch.
+ The closemap is a file that allows closing of a branch. This is useful if
+ you want to close a branch. Each entry contains a revision or hash
+ separated by white space.
+
+ The tagpmap is a file that exactly analogous to the branchmap. This will
+ rename tags on the fly and prevent the 'update tags' commit usually found
+ at the end of a convert process.
+
Mercurial Source
################
@@ -266,6 +274,8 @@
--filemap FILE remap file names using contents of file
--splicemap FILE splice synthesized history into place
--branchmap FILE change branch names while converting
+ --closemap FILE closes given revs
+ --tagmap FILE change tag names while converting
--branchsort try to sort changesets by branches
--datesort try to sort changesets by date
--sourcesort preserve source changesets order
--- a/tests/test-default-push.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-default-push.t Thu Feb 27 18:57:03 2014 -0600
@@ -15,6 +15,16 @@
$ echo b >> b/a
$ hg --cwd b ci -mb
+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]
+
+ $ cd ..
+
Push should push to 'default' when 'default-push' not set:
$ hg --cwd b push
--- a/tests/test-doctest.py Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-doctest.py Thu Feb 27 18:57:03 2014 -0600
@@ -27,3 +27,4 @@
testmod('mercurial.util', testtarget='platform')
testmod('hgext.convert.cvsps')
testmod('hgext.convert.filemap')
+testmod('hgext.convert.subversion')
--- a/tests/test-gendoc.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-gendoc.t Thu Feb 27 18:57:03 2014 -0600
@@ -3,7 +3,7 @@
$ "$TESTDIR/hghave" docutils || exit 80
$ HGENCODING=UTF-8
$ export HGENCODING
- $ { echo C; find "$TESTDIR/../i18n" -name "*.po" | sort; } | while read PO; do
+ $ { echo C; ls "$TESTDIR/../i18n"/*.po | sort; } | while read PO; do
> LOCALE=`basename "$PO" .po`
> echo
> echo "% extracting documentation from $LOCALE"
--- a/tests/test-globalopts.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-globalopts.t Thu Feb 27 18:57:03 2014 -0600
@@ -297,6 +297,7 @@
cat output the current or given revision of files
clone make a copy of an existing repository
commit commit the specified files or all outstanding changes
+ config show combined config settings from all hgrc files
copy mark files as copied for the next commit
diff diff repository (or selected files)
export dump the header and diffs for one or more changesets
@@ -326,7 +327,6 @@
revert restore files to their checkout state
root print the root (top) of the current working directory
serve start stand-alone webserver
- showconfig show combined config settings from all hgrc files
status show changed files in the working directory
summary summarize working directory state
tag add one or more tags for the current or given revision
@@ -379,6 +379,7 @@
cat output the current or given revision of files
clone make a copy of an existing repository
commit commit the specified files or all outstanding changes
+ config show combined config settings from all hgrc files
copy mark files as copied for the next commit
diff diff repository (or selected files)
export dump the header and diffs for one or more changesets
@@ -408,7 +409,6 @@
revert restore files to their checkout state
root print the root (top) of the current working directory
serve start stand-alone webserver
- showconfig show combined config settings from all hgrc files
status show changed files in the working directory
summary summarize working directory state
tag add one or more tags for the current or given revision
--- a/tests/test-help.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-help.t Thu Feb 27 18:57:03 2014 -0600
@@ -62,6 +62,7 @@
cat output the current or given revision of files
clone make a copy of an existing repository
commit commit the specified files or all outstanding changes
+ config show combined config settings from all hgrc files
copy mark files as copied for the next commit
diff diff repository (or selected files)
export dump the header and diffs for one or more changesets
@@ -91,7 +92,6 @@
revert restore files to their checkout state
root print the root (top) of the current working directory
serve start stand-alone webserver
- showconfig show combined config settings from all hgrc files
status show changed files in the working directory
summary summarize working directory state
tag add one or more tags for the current or given revision
@@ -138,6 +138,7 @@
cat output the current or given revision of files
clone make a copy of an existing repository
commit commit the specified files or all outstanding changes
+ config show combined config settings from all hgrc files
copy mark files as copied for the next commit
diff diff repository (or selected files)
export dump the header and diffs for one or more changesets
@@ -167,7 +168,6 @@
revert restore files to their checkout state
root print the root (top) of the current working directory
serve start stand-alone webserver
- showconfig show combined config settings from all hgrc files
status show changed files in the working directory
summary summarize working directory state
tag add one or more tags for the current or given revision
@@ -198,6 +198,89 @@
templating Template Usage
urls URL Paths
+Test extension help:
+ $ hg help extensions --config extensions.rebase= --config extensions.children=
+ Using Additional Features
+ """""""""""""""""""""""""
+
+ Mercurial has the ability to add new features through the use of
+ extensions. Extensions may add new commands, add options to existing
+ commands, change the default behavior of commands, or implement hooks.
+
+ To enable the "foo" extension, either shipped with Mercurial or in the
+ Python search path, create an entry for it in your configuration file,
+ like this:
+
+ [extensions]
+ foo =
+
+ You may also specify the full path to an extension:
+
+ [extensions]
+ myfeature = ~/.hgext/myfeature.py
+
+ See "hg help config" for more information on configuration files.
+
+ Extensions are not loaded by default for a variety of reasons: they can
+ increase startup overhead; they may be meant for advanced usage only; they
+ may provide potentially dangerous abilities (such as letting you destroy
+ or modify history); they might not be ready for prime time; or they may
+ alter some usual behaviors of stock Mercurial. It is thus up to the user
+ to activate extensions as needed.
+
+ To explicitly disable an extension enabled in a configuration file of
+ broader scope, prepend its path with !:
+
+ [extensions]
+ # disabling extension bar residing in /path/to/extension/bar.py
+ bar = !/path/to/extension/bar.py
+ # ditto, but no path was supplied for extension baz
+ baz = !
+
+ enabled extensions:
+
+ children command to display child changesets (DEPRECATED)
+ rebase command to move sets of revisions to a different ancestor
+
+ disabled extensions:
+
+ acl hooks for controlling repository access
+ blackbox log repository events to a blackbox for debugging
+ bugzilla hooks for integrating with the Bugzilla bug tracker
+ churn command to display statistics about repository history
+ color colorize output from some commands
+ convert import revisions from foreign VCS repositories into
+ Mercurial
+ eol automatically manage newlines in repository files
+ extdiff command to allow external programs to compare revisions
+ factotum http authentication with factotum
+ gpg commands to sign and verify changesets
+ hgcia hooks for integrating with the CIA.vc notification service
+ hgk browse the repository in a graphical way
+ highlight syntax highlighting for hgweb (requires Pygments)
+ histedit interactive history editing
+ inotify accelerate status report using Linux's inotify service
+ interhg expand expressions into changelog and summaries
+ keyword expand keywords in tracked files
+ largefiles track large binary files
+ mq manage a stack of patches
+ notify hooks for sending email push notifications
+ pager browse command output with an external pager
+ patchbomb command to send changesets as (a series of) patch emails
+ progress show progress bars for some actions
+ purge command to delete untracked files from the working
+ directory
+ record commands to interactively select changes for
+ commit/qrefresh
+ relink recreates hardlinks between repository clones
+ schemes extend schemes with shortcuts to repository swarms
+ share share a common history between several working directories
+ shelve save and restore changes to the working directory
+ strip strip changesets and their descendents from history
+ transplant command to transplant changesets from another branch
+ win32mbcs allow the use of MBCS paths with problematic encodings
+ win32text perform automatic newline conversion
+ zeroconf discover and advertise repositories on the local network
Test short command list with verbose option
$ hg -v help shortlist
@@ -623,6 +706,7 @@
cat output the current or given revision of files
clone make a copy of an existing repository
commit commit the specified files or all outstanding changes
+ config show combined config settings from all hgrc files
copy mark files as copied for the next commit
diff diff repository (or selected files)
export dump the header and diffs for one or more changesets
@@ -652,7 +736,6 @@
revert restore files to their checkout state
root print the root (top) of the current working directory
serve start stand-alone webserver
- showconfig show combined config settings from all hgrc files
status show changed files in the working directory
summary summarize working directory state
tag add one or more tags for the current or given revision
@@ -1259,6 +1342,13 @@
output the current or given revision of files
</td></tr>
<tr><td>
+ <a href="/help/config">
+ config
+ </a>
+ </td><td>
+ show combined config settings from all hgrc files
+ </td></tr>
+ <tr><td>
<a href="/help/copy">
copy
</a>
@@ -1399,13 +1489,6 @@
print the root (top) of the current working directory
</td></tr>
<tr><td>
- <a href="/help/showconfig">
- showconfig
- </a>
- </td><td>
- show combined config settings from all hgrc files
- </td></tr>
- <tr><td>
<a href="/help/tag">
tag
</a>
--- a/tests/test-histedit-arguments.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-histedit-arguments.t Thu Feb 27 18:57:03 2014 -0600
@@ -51,10 +51,12 @@
# Edit history between eb57da33312f and 08d98a8350f3
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
--- a/tests/test-histedit-bookmark-motion.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-histedit-bookmark-motion.t Thu Feb 27 18:57:03 2014 -0600
@@ -67,10 +67,12 @@
# Edit history between d2ae7f538514 and 652413bf663e
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
@@ -125,10 +127,12 @@
# Edit history between b346ab9a313d and cacdfd884a93
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
--- a/tests/test-histedit-commute.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-histedit-commute.t Thu Feb 27 18:57:03 2014 -0600
@@ -61,10 +61,12 @@
# Edit history between 177f92b77385 and 652413bf663e
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
--- a/tests/test-histedit-obsolete.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-histedit-obsolete.t Thu Feb 27 18:57:03 2014 -0600
@@ -51,10 +51,12 @@
# Edit history between d2ae7f538514 and 652413bf663e
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
--- a/tests/test-histedit-outgoing.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-histedit-outgoing.t Thu Feb 27 18:57:03 2014 -0600
@@ -43,10 +43,12 @@
# Edit history between 055a42cdd887 and 652413bf663e
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
@@ -72,10 +74,12 @@
# Edit history between 2a4042b45417 and 51281e65ba79
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
@@ -93,10 +97,12 @@
# Edit history between f26599ee3441 and f26599ee3441
#
+ # Commits are listed from least to most recent
+ #
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit (combines N and N-1)
+ # f, fold = use commit, but combine it with the one above
# d, drop = remove commit from history
# m, mess = edit message without changing commit content
#
--- a/tests/test-hook.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-hook.t Thu Feb 27 18:57:03 2014 -0600
@@ -499,7 +499,7 @@
$ echo >> foo
$ hg ci --debug -d '0 0' -m 'change foo'
foo
- calling hook commit.auto: <function autohook at *> (glob)
+ calling hook commit.auto: hgext_hookext.autohook
Automatically installed hook
committed changeset 1:52998019f6252a2b893452765fcb0a47351a5708
--- a/tests/test-http.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-http.t Thu Feb 27 18:57:03 2014 -0600
@@ -153,7 +153,8 @@
> common.permhooks.insert(0, perform_authentication)
> EOT
$ hg --config extensions.x=userpass.py serve -p $HGPORT2 -d --pid-file=pid \
- > --config server.preferuncompressed=True
+ > --config server.preferuncompressed=True \
+ > --config web.push_ssl=False --config web.allow_push=* -A ../access.log
$ cat pid >> $DAEMON_PIDS
$ cat << EOF > get_pass.py
@@ -163,6 +164,7 @@
> getpass.getpass = newgetpass
> EOF
+#if python243
$ hg id http://localhost:$HGPORT2/
abort: http authorization required for http://localhost:$HGPORT2/
[255]
@@ -176,6 +178,7 @@
password: 5fed3813f7f5
$ hg id http://user:pass@localhost:$HGPORT2/
5fed3813f7f5
+#endif
$ echo '[auth]' >> .hg/hgrc
$ echo 'l.schemes=http' >> .hg/hgrc
$ echo 'l.prefix=lo' >> .hg/hgrc
@@ -187,6 +190,7 @@
5fed3813f7f5
$ hg id http://user@localhost:$HGPORT2/
5fed3813f7f5
+#if python243
$ hg clone http://user:pass@localhost:$HGPORT2/ dest 2>&1
streaming all changes
7 files to transfer, 916 bytes of data
@@ -201,6 +205,82 @@
abort: HTTP Error 403: no
[255]
+ $ hg -R dest tag -r tip top
+ $ hg -R dest push http://user:pass@localhost:$HGPORT2/
+ pushing to http://user:***@localhost:$HGPORT2/
+ searching for changes
+ remote: adding changesets
+ remote: adding manifests
+ remote: adding file changes
+ remote: added 1 changesets with 1 changes to 1 files
+ $ hg rollback -q
+
+ $ cut -c38- ../access.log
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=branchmap HTTP/1.1" 200 -
+ "GET /?cmd=stream_out HTTP/1.1" 401 -
+ "GET /?cmd=stream_out HTTP/1.1" 200 -
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=lookup HTTP/1.1" 200 - x-hgarg-1:key=tip
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=listkeys HTTP/1.1" 403 - x-hgarg-1:namespace=namespaces
+ "GET /?cmd=capabilities HTTP/1.1" 200 -
+ "GET /?cmd=batch HTTP/1.1" 200 - x-hgarg-1:cmds=heads+%3Bknown+nodes%3D7f4e523d01f2cc3765ac8934da3d14db775ff872
+ "GET /?cmd=branchmap HTTP/1.1" 200 -
+ "GET /?cmd=branchmap HTTP/1.1" 200 -
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
+ "POST /?cmd=unbundle HTTP/1.1" 401 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524
+ "POST /?cmd=unbundle HTTP/1.1" 200 - x-hgarg-1:heads=686173686564+5eb5abfefeea63c80dd7553bcc3783f37e0c5524
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=phases
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=phases
+ "POST /?cmd=pushkey HTTP/1.1" 401 - x-hgarg-1:key=7f4e523d01f2cc3765ac8934da3d14db775ff872&namespace=phases&new=0&old=1
+ "POST /?cmd=pushkey HTTP/1.1" 200 - x-hgarg-1:key=7f4e523d01f2cc3765ac8934da3d14db775ff872&namespace=phases&new=0&old=1
+ "GET /?cmd=listkeys HTTP/1.1" 401 - x-hgarg-1:namespace=bookmarks
+ "GET /?cmd=listkeys HTTP/1.1" 200 - x-hgarg-1:namespace=bookmarks
+
+#endif
$ cd ..
clone of serve with repo in root and unserved subrepo (issue2970)
--- a/tests/test-install.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-install.t Thu Feb 27 18:57:03 2014 -0600
@@ -16,7 +16,7 @@
checking templates (*mercurial?templates)... (glob)
checking commit editor...
checking username...
- no username supplied (see "hg help config")
+ no username supplied
(specify a username in your configuration file)
1 problems detected, please check your install!
[1]
--- a/tests/test-largefiles-cache.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-largefiles-cache.t Thu Feb 27 18:57:03 2014 -0600
@@ -47,7 +47,7 @@
$ hg update -r0
getting changed largefiles
- large: largefile 7f7097b041ccf68cc5561e9600da4655d21c6d18 not available from file://$TESTTMP/mirror (glob)
+ large: largefile 7f7097b041ccf68cc5561e9600da4655d21c6d18 not available from file:/*/$TESTTMP/mirror (glob)
0 largefiles updated, 0 removed
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg status
@@ -64,7 +64,7 @@
$ hg update -r0
getting changed largefiles
- large: largefile 7f7097b041ccf68cc5561e9600da4655d21c6d18 not available from file://$TESTTMP/mirror (glob)
+ large: largefile 7f7097b041ccf68cc5561e9600da4655d21c6d18 not available from file:/*/$TESTTMP/mirror (glob)
0 largefiles updated, 0 removed
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg status
--- a/tests/test-largefiles.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-largefiles.t Thu Feb 27 18:57:03 2014 -0600
@@ -900,7 +900,7 @@
adding manifests
adding file changes
added 6 changesets with 16 changes to 8 files
- calling hook changegroup.lfiles: <function checkrequireslfiles at *> (glob)
+ calling hook changegroup.lfiles: hgext.largefiles.reposetup.checkrequireslfiles
(run 'hg update' to get a working copy)
pulling largefiles for revision 7
found 971fb41e78fea4f8e0ba5244784239371cb00591 in store
@@ -1280,7 +1280,7 @@
$ rm ${USERCACHE}/7838695e10da2bb75ac1156565f40a2595fa2fa0
$ hg up -r 6
getting changed largefiles
- large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file://$TESTTMP/d (glob)
+ large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1 largefiles updated, 2 removed
4 files updated, 0 files merged, 2 files removed, 0 files unresolved
$ rm normal3
@@ -1301,7 +1301,7 @@
! normal3
$ hg up -Cr.
getting changed largefiles
- large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file://$TESTTMP/d (glob)
+ large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
0 largefiles updated, 0 removed
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg st
@@ -1323,7 +1323,7 @@
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
getting changed largefiles
- large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file://$TESTTMP/d (glob)
+ large3: largefile 7838695e10da2bb75ac1156565f40a2595fa2fa0 not available from file:/*/$TESTTMP/d (glob)
1 largefiles updated, 0 removed
$ hg rollback -q
--- a/tests/test-lfconvert.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-lfconvert.t Thu Feb 27 18:57:03 2014 -0600
@@ -342,7 +342,7 @@
$ rm largefiles-repo/.hg/largefiles/*
$ hg lfconvert --to-normal issue3519 normalized3519
initializing destination normalized3519
- large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file://$TESTTMP/largefiles-repo (glob)
+ large: largefile 2e000fa7e85759c7f4c254d4d9c33ef481e459a7 not available from file:/*/$TESTTMP/largefiles-repo (glob)
abort: missing largefile 'large' from revision d4892ec57ce212905215fad1d9018f56b99202ad
[255]
--- a/tests/test-lock-badness.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-lock-badness.t Thu Feb 27 18:57:03 2014 -0600
@@ -1,4 +1,7 @@
-#if unix-permissions no-root
+#if unix-permissions no-root no-windows
+
+Prepare
+
$ hg init a
$ echo a > a/a
$ hg -R a ci -A -m a
@@ -8,10 +11,25 @@
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+One process waiting for another
+
+ $ cat > hooks.py << EOF
+ > import time
+ > def sleepone(**x): time.sleep(1)
+ > def sleephalf(**x): time.sleep(0.5)
+ > EOF
$ echo b > b/b
- $ hg -R b ci -A -m b
+ $ hg -R b ci -A -m b --config hooks.precommit="python:`pwd`/hooks.py:sleepone" > stdout &
+ $ hg -R b up -q --config hooks.pre-update="python:`pwd`/hooks.py:sleephalf"
+ waiting for lock on working directory of b held by '*:*' (glob)
+ got lock after 1 seconds
+ warning: ignoring unknown working parent d2ae7f538514!
+ $ wait
+ $ cat stdout
adding b
+Pushing to a local read-only repo that can't be locked
+
$ chmod 100 a/.hg/store
$ hg -R b push a
--- a/tests/test-module-imports.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-module-imports.t Thu Feb 27 18:57:03 2014 -0600
@@ -22,18 +22,20 @@
hidden by deduplication algorithm in the cycle detector, so fixing
these may expose other cycles.
- $ hg locate 'mercurial/**.py' | xargs python "$import_checker"
- mercurial/dispatch.py mixed stdlib and relative imports:
- commands, error, extensions, fancyopts, hg, hook, util
- mercurial/fileset.py mixed stdlib and relative imports:
- error, merge, parser, util
- mercurial/revset.py mixed stdlib and relative imports:
- discovery, error, hbisect, parser, phases, util
- mercurial/templater.py mixed stdlib and relative imports:
- config, error, parser, templatefilters, util
- mercurial/ui.py mixed stdlib and relative imports:
- config, error, formatter, scmutil, util
- Import cycle: mercurial.cmdutil -> mercurial.subrepo -> mercurial.cmdutil
- Import cycle: mercurial.repoview -> mercurial.revset -> mercurial.repoview
- Import cycle: mercurial.fileset -> mercurial.merge -> mercurial.subrepo -> mercurial.match -> mercurial.fileset
- Import cycle: mercurial.filemerge -> mercurial.match -> mercurial.fileset -> mercurial.merge -> mercurial.filemerge
+ $ hg locate 'mercurial/**.py' | sed 's-\\-/-g' | xargs 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: discovery, 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, scmutil, util
+ Import cycle: mercurial.cmdutil -> mercurial.context -> mercurial.subrepo -> mercurial.cmdutil -> mercurial.cmdutil
--- a/tests/test-push-warn.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-push-warn.t Thu Feb 27 18:57:03 2014 -0600
@@ -22,6 +22,7 @@
$ hg push ../a
pushing to ../a
searching for changes
+ remote has heads on branch 'default' that are not known locally: 1c9246a22a0a
abort: push creates new remote head 1e108cc5548c!
(pull and merge or see "hg help push" for details about pushing new heads)
[255]
@@ -35,6 +36,7 @@
query 2; still undecided: 1, sample size is: 1
2 total queries
listing keys for "bookmarks"
+ remote has heads on branch 'default' that are not known locally: 1c9246a22a0a
new remote heads on branch 'default':
1e108cc5548c
abort: push creates new remote head 1e108cc5548c!
@@ -405,6 +407,7 @@
$ hg -R i push h
pushing to h
searching for changes
+ remote has heads on branch 'default' that are not known locally: ce4212fc8847
abort: push creates new remote head 97bd0c84d346!
(pull and merge or see "hg help push" for details about pushing new heads)
[255]
--- a/tests/test-rebase-conflicts.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-rebase-conflicts.t Thu Feb 27 18:57:03 2014 -0600
@@ -210,7 +210,7 @@
summary: added default.txt
$ hg rebase -s9 -d2 --debug # use debug to really check merge base used
- rebase onto 2 starting from [<changectx e31216eec445>]
+ rebase onto 2 starting from e31216eec445
rebasing: 9:e31216eec445 5/6 changesets (83.33%)
future parents are 2 and -1
rebase status stored
--- a/tests/test-record.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-record.t Thu Feb 27 18:57:03 2014 -0600
@@ -251,7 +251,8 @@
$ echo 11 >> plain
$ unset HGUSER
$ hg record --config ui.username= -d '8 0' -m end plain
- abort: no username supplied (see "hg help config")
+ abort: no username supplied
+ (use "hg config --edit" to set your username)
[255]
--- a/tests/test-revset.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-revset.t Thu Feb 27 18:57:03 2014 -0600
@@ -414,6 +414,16 @@
2
1
0
+ $ log '1:: and reverse(all())'
+ 9
+ 8
+ 7
+ 6
+ 5
+ 4
+ 3
+ 2
+ 1
$ log 'rev(5)'
5
$ log 'sort(limit(reverse(all()), 3))'
@@ -434,6 +444,70 @@
$ log 'tag(tip)'
9
+check that conversion to _missingancestors works
+ $ try --optimize '::3 - ::1'
+ (minus
+ (dagrangepre
+ ('symbol', '3'))
+ (dagrangepre
+ ('symbol', '1')))
+ * optimized:
+ (func
+ ('symbol', '_missingancestors')
+ (list
+ ('symbol', '3')
+ ('symbol', '1')))
+ 3
+ $ try --optimize 'ancestors(1) - ancestors(3)'
+ (minus
+ (func
+ ('symbol', 'ancestors')
+ ('symbol', '1'))
+ (func
+ ('symbol', 'ancestors')
+ ('symbol', '3')))
+ * optimized:
+ (func
+ ('symbol', '_missingancestors')
+ (list
+ ('symbol', '1')
+ ('symbol', '3')))
+ $ try --optimize 'not ::2 and ::6'
+ (and
+ (not
+ (dagrangepre
+ ('symbol', '2')))
+ (dagrangepre
+ ('symbol', '6')))
+ * optimized:
+ (func
+ ('symbol', '_missingancestors')
+ (list
+ ('symbol', '6')
+ ('symbol', '2')))
+ 3
+ 4
+ 5
+ 6
+ $ try --optimize 'ancestors(6) and not ancestors(4)'
+ (and
+ (func
+ ('symbol', 'ancestors')
+ ('symbol', '6'))
+ (not
+ (func
+ ('symbol', 'ancestors')
+ ('symbol', '4'))))
+ * optimized:
+ (func
+ ('symbol', '_missingancestors')
+ (list
+ ('symbol', '6')
+ ('symbol', '4')))
+ 3
+ 5
+ 6
+
we can use patterns when searching for tags
$ log 'tag("1..*")'
--- a/tests/test-rollback.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-rollback.t Thu Feb 27 18:57:03 2014 -0600
@@ -184,4 +184,14 @@
$ cat a
a
- $ cd ..
+corrupt journal test
+ $ echo "foo" > .hg/store/journal
+ $ hg recover
+ rolling back interrupted transaction
+ couldn't read journal entry 'foo\n'!
+ checking changesets
+ checking manifests
+ crosschecking files in changesets and manifests
+ checking files
+ 1 files, 2 changesets, 2 total revisions
+
--- a/tests/test-shelve.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-shelve.t Thu Feb 27 18:57:03 2014 -0600
@@ -23,10 +23,6 @@
$ hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 5 changes to 5 files
$ hg commit -q -m 'initial commit'
@@ -81,11 +77,11 @@
ensure that our shelved changes exist
$ hg shelve -l
- default-01 (*) [mq]: second.patch (glob)
- default (*) [mq]: second.patch (glob)
+ default-01 (*) changes to '[mq]: second.patch' (glob)
+ default (*) changes to '[mq]: second.patch' (glob)
$ hg shelve -l -p default
- default (*) [mq]: second.patch (glob)
+ default (*) changes to '[mq]: second.patch' (glob)
diff --git a/a/a b/a/a
--- a/a/a
@@ -104,10 +100,8 @@
$ printf "z\na\n" > a/a
$ hg unshelve --keep
unshelving change 'default-01'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 3 changes to 8 files (+1 heads)
+ temporarily committing pending changes (restore with 'hg unshelve --abort')
+ rebasing shelved changes
merging a/a
$ hg revert --all -q
@@ -117,10 +111,6 @@
$ hg unshelve
unshelving change 'default-01'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 3 changes to 8 files
$ hg status -C
M a/a
A b.rename/b
@@ -192,10 +182,8 @@
$ hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 3 changes to 8 files (+1 heads)
+ temporarily committing pending changes (restore with 'hg unshelve --abort')
+ rebasing shelved changes
merging a/a
warning: conflicts during merge.
merging a/a incomplete! (edit conflicts, then use 'hg resolve --mark')
@@ -379,10 +367,8 @@
$ HGMERGE=true hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 1 changes to 6 files (+1 heads)
+ temporarily committing pending changes (restore with 'hg unshelve --abort')
+ rebasing shelved changes
merging a/a
$ hg parents -q
4:33f7f61e6c5e
@@ -400,15 +386,11 @@
shelved as default
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ hg shelve --list
- default (*) create conflict (glob)
+ default (*) changes to 'create conflict' (glob)
$ hg unshelve --keep
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 1 changes to 7 files
$ hg shelve --list
- default (*) create conflict (glob)
+ default (*) changes to 'create conflict' (glob)
$ hg shelve --cleanup
$ hg shelve --list
@@ -424,10 +406,6 @@
* test 4:33f7f61e6c5e
$ hg unshelve
unshelving change 'test'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 1 changes to 7 files
$ hg bookmark
* test 4:33f7f61e6c5e
@@ -437,13 +415,9 @@
shelved as test
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ hg --config extensions.mq=! shelve --list
- test (1s ago) create conflict
+ test (*) changes to 'create conflict' (glob)
$ hg --config extensions.mq=! unshelve
unshelving change 'test'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 1 changes to 7 files
shelve should leave dirstate clean (issue 4055)
@@ -468,10 +442,7 @@
saved backup bundle to $TESTTMP/shelverebase/.hg/strip-backup/323bfa07f744-backup.hg (glob)
$ hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 2 changesets with 2 changes to 2 files (+1 heads)
+ rebasing shelved changes
$ hg status
M z
@@ -497,10 +468,7 @@
$ hg up -q 1
$ hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 1 changes to 3 files
+ rebasing shelved changes
$ hg status
A d
@@ -513,10 +481,7 @@
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 1 changes to 3 files
+ rebasing shelved changes
$ hg status
A d
@@ -534,10 +499,6 @@
$ hg debugobsolete `hg --debug id -i -r 1`
$ hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 1 changes to 2 files (+1 heads)
unshelve should leave unknown files alone (issue4113)
@@ -549,10 +510,6 @@
? e
$ hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 1 changes to 2 files (+1 heads)
$ hg status
A d
? e
@@ -568,13 +525,138 @@
$ echo z > e
$ hg unshelve
unshelving change 'default'
- adding changesets
- adding manifests
- adding file changes
- added 1 changesets with 2 changes to 3 files (+1 heads)
$ cat e
e
$ cat e.orig
z
+
+unshelve and conflicts with untracked files
+
+ preparing:
+
+ $ rm *.orig
+ $ hg ci -qm 'commit stuff'
+ $ hg phase -p null:
+
+ no other changes - no merge:
+
+ $ echo f > f
+ $ hg add f
+ $ hg shelve
+ shelved as default
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo gold > f
+ $ hg unshelve
+ unshelving change 'default'
+ $ hg st
+ A f
+ ? f.orig
+ $ cat f
+ f
+ $ cat f.orig
+ gold
+
+ other uncommitted changes - merge:
+
+ $ hg st
+ A f
+ ? f.orig
+ $ hg shelve
+ shelved as default
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg log -G --template '{rev} {desc|firstline} {author}' -R bundle://.hg/shelved/default.hg -r 'bundle()'
+ o 4 changes to 'commit stuff' shelve@localhost
+ |
+ $ hg log -G --template '{rev} {desc|firstline} {author}'
+ @ 3 commit stuff test
+ |
+ | o 2 c test
+ |/
+ o 0 a test
+
+ $ mv f.orig f
+ $ echo other change >> a
+ $ hg unshelve
+ unshelving change 'default'
+ temporarily committing pending changes (restore with 'hg unshelve --abort')
+ rebasing shelved changes
+ merging f
+ warning: conflicts during merge.
+ merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+ unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
+ [1]
+ $ hg log -G --template '{rev} {desc|firstline} {author}'
+ @ 5 changes to 'commit stuff' shelve@localhost
+ |
+ | @ 4 pending changes temporary commit shelve@localhost
+ |/
+ o 3 commit stuff test
+ |
+ | o 2 c test
+ |/
+ o 0 a test
+
+ $ hg st
+ M f
+ ? f.orig
+ $ cat f
+ <<<<<<< local
+ gold
+ =======
+ f
+ >>>>>>> other
+ $ cat f.orig
+ gold
+ $ hg unshelve --abort
+ rebase aborted
+ unshelve of 'default' aborted
+ $ hg st
+ M a
+ ? f.orig
+ $ cat f.orig
+ gold
+ $ hg unshelve
+ unshelving change 'default'
+ temporarily committing pending changes (restore with 'hg unshelve --abort')
+ rebasing shelved changes
+ $ hg st
+ M a
+ A f
+ ? f.orig
+
+ other committed changes - merge:
+
+ $ hg shelve f
+ shelved as default
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg ci a -m 'intermediate other change'
+ $ mv f.orig f
+ $ hg unshelve
+ unshelving change 'default'
+ rebasing shelved changes
+ merging f
+ warning: conflicts during merge.
+ merging f incomplete! (edit conflicts, then use 'hg resolve --mark')
+ unresolved conflicts (see 'hg resolve', then 'hg unshelve --continue')
+ [1]
+ $ hg st
+ M f
+ ? f.orig
+ $ cat f
+ <<<<<<< local
+ gold
+ =======
+ f
+ >>>>>>> other
+ $ cat f.orig
+ gold
+ $ hg unshelve --abort
+ rebase aborted
+ no changes needed to a
+ no changes needed to d
+ no changes needed to e
+ unshelve of 'default' aborted
+ $ hg shelve --delete default
+
$ cd ..
--- a/tests/test-ssh.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-ssh.t Thu Feb 27 18:57:03 2014 -0600
@@ -223,7 +223,7 @@
$ hg push
pushing to ssh://user@dummy/remote
searching for changes
- note: unsynced remote changes!
+ remote has heads on branch 'default' that are not known locally: 6c0482d977a3
remote: adding changesets
remote: adding manifests
remote: adding file changes
--- a/tests/test-status-color.t Tue Feb 25 18:45:01 2014 -0800
+++ b/tests/test-status-color.t Thu Feb 27 18:57:03 2014 -0600
@@ -1,5 +1,3 @@
- $ "$TESTDIR/hghave" tic || exit 80
-
$ echo "[extensions]" >> $HGRCPATH
$ echo "color=" >> $HGRCPATH
$ echo "[color]" >> $HGRCPATH
@@ -186,8 +184,11 @@
\x1b[0;0mC \x1b[0m\x1b[0;0m.hgignore\x1b[0m (esc)
\x1b[0;0mC \x1b[0m\x1b[0;0mmodified\x1b[0m (esc)
+
hg status -A (with terminfo color):
+#if tic
+
$ mkdir "$TESTTMP/terminfo"
$ TERMINFO="$TESTTMP/terminfo" tic "$TESTDIR/hgterm.ti"
$ TERM=hgterm TERMINFO="$TESTTMP/terminfo" hg status --config color.mode=terminfo --color=always -A
@@ -201,6 +202,8 @@
\x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30m.hgignore\x1b[30m (esc)
\x1b[30m\x1b[30mC \x1b[30m\x1b[30m\x1b[30mmodified\x1b[30m (esc)
+#endif
+
$ echo "^ignoreddir$" > .hgignore
$ mkdir ignoreddir