--- a/.hgignore Wed Mar 23 13:58:33 2011 -0300
+++ b/.hgignore Wed Mar 23 12:38:36 2011 -0500
@@ -7,6 +7,7 @@
*.mergebackup
*.o
*.so
+*.dll
*.pyd
*.pyc
*.pyo
--- a/contrib/check-code.py Wed Mar 23 13:58:33 2011 -0300
+++ b/contrib/check-code.py Wed Mar 23 12:38:36 2011 -0500
@@ -66,6 +66,7 @@
(r'^source\b', "don't use 'source', use '.'"),
(r'touch -d', "don't use 'touch -d', use 'touch -t' instead"),
(r'ls\s+[^|-]+\s+-', "options to 'ls' must come before filenames"),
+ (r'[^>]>\s*\$HGRCPATH', "don't overwrite $HGRCPATH, append to it"),
]
testfilters = [
@@ -176,7 +177,7 @@
(r'\([^\)]+\) \w+', "use (int)foo, not (int) foo"),
(r'\S+ (\+\+|--)', "use foo++, not foo ++"),
(r'\w,\w', "missing whitespace after ,"),
- (r'\w[+/*]\w', "missing whitespace in expression"),
+ (r'^[^#]\w[+/*]\w', "missing whitespace in expression"),
(r'^#\s+\w', "use #foo, not # foo"),
(r'[^\n]\Z', "no trailing newline"),
]
--- a/contrib/zsh_completion Wed Mar 23 13:58:33 2011 -0300
+++ b/contrib/zsh_completion Wed Mar 23 12:38:36 2011 -0500
@@ -360,8 +360,8 @@
'(--help -h)'{-h,--help}'[display help and exit]'
'--debug[debug mode]'
'--debugger[start debugger]'
- '--encoding[set the charset encoding (default: UTF8)]'
- '--encodingmode[set the charset encoding mode (default: strict)]'
+ '--encoding[set the charset encoding]'
+ '--encodingmode[set the charset encoding mode]'
'--lsprof[print improved command execution profile]'
'--traceback[print traceback on exception]'
'--time[time how long the command takes]'
--- a/hgext/color.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/color.py Wed Mar 23 12:38:36 2011 -0500
@@ -18,11 +18,11 @@
'''colorize output from some commands
-This extension modifies the status and resolve commands to add color to their
-output to reflect file status, the qseries command to add color to reflect
-patch status (applied, unapplied, missing), and to diff-related
-commands to highlight additions, removals, diff headers, and trailing
-whitespace.
+This extension modifies the status and resolve commands to add color
+to their output to reflect file status, the qseries command to add
+color to reflect patch status (applied, unapplied, missing), and to
+diff-related commands to highlight additions, removals, diff headers,
+and trailing whitespace.
Other effects in addition to color, like bold and underlined text, are
also available. Effects are rendered with the ECMA-48 SGR control
--- a/hgext/convert/__init__.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/convert/__init__.py Wed Mar 23 12:38:36 2011 -0500
@@ -10,7 +10,7 @@
import convcmd
import cvsps
import subversion
-from mercurial import commands
+from mercurial import commands, templatekw
from mercurial.i18n import _
# Commands definition was moved elsewhere to ease demandload job.
@@ -334,3 +334,34 @@
],
_('hg debugcvsps [OPTION]... [PATH]...')),
}
+
+def kwconverted(ctx, name):
+ rev = ctx.extra().get('convert_revision', '')
+ if rev.startswith('svn:'):
+ if name == 'svnrev':
+ return str(subversion.revsplit(rev)[2])
+ elif name == 'svnpath':
+ return subversion.revsplit(rev)[1]
+ elif name == 'svnuuid':
+ return subversion.revsplit(rev)[0]
+ return rev
+
+def kwsvnrev(repo, ctx, **args):
+ """:svnrev: String. Converted subversion revision number."""
+ return kwconverted(ctx, 'svnrev')
+
+def kwsvnpath(repo, ctx, **args):
+ """:svnpath: String. Converted subversion revision project path."""
+ return kwconverted(ctx, 'svnpath')
+
+def kwsvnuuid(repo, ctx, **args):
+ """:svnuuid: String. Converted subversion revision repository identifier."""
+ return kwconverted(ctx, 'svnuuid')
+
+def extsetup(ui):
+ templatekw.keywords['svnrev'] = kwsvnrev
+ templatekw.keywords['svnpath'] = kwsvnpath
+ templatekw.keywords['svnuuid'] = kwsvnuuid
+
+# tell hggettext to extract docstrings from these functions:
+i18nfunctions = [kwsvnrev, kwsvnpath, kwsvnuuid]
--- a/hgext/convert/subversion.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/convert/subversion.py Wed Mar 23 12:38:36 2011 -0500
@@ -41,6 +41,15 @@
class SvnPathNotFound(Exception):
pass
+def revsplit(rev):
+ """Parse a revision string and return (uuid, path, revnum)."""
+ url, revnum = rev.rsplit('@', 1)
+ parts = url.split('/', 1)
+ mod = ''
+ if len(parts) > 1:
+ mod = '/' + parts[1]
+ return parts[0][4:], mod, int(revnum)
+
def geturl(path):
try:
return svn.client.url_from_path(svn.core.svn_path_canonicalize(path))
@@ -259,6 +268,7 @@
except ValueError:
raise util.Abort(_('svn: revision %s is not an integer') % rev)
+ self.trunkname = self.ui.config('convert', 'svn.trunk', 'trunk').strip('/')
self.startrev = self.ui.config('convert', 'svn.startrev', default=0)
try:
self.startrev = int(self.startrev)
@@ -285,7 +295,7 @@
def setrevmap(self, revmap):
lastrevs = {}
for revid in revmap.iterkeys():
- uuid, module, revnum = self.revsplit(revid)
+ uuid, module, revnum = revsplit(revid)
lastrevnum = lastrevs.setdefault(module, revnum)
if revnum > lastrevnum:
lastrevs[module] = revnum
@@ -380,7 +390,7 @@
files, self.removed, copies = self.expandpaths(rev, paths, parents)
else:
# Perform a full checkout on roots
- uuid, module, revnum = self.revsplit(rev)
+ uuid, module, revnum = revsplit(rev)
entries = svn.client.ls(self.baseurl + urllib.quote(module),
optrev(revnum), True, self.ctx)
files = [n for n, e in entries.iteritems()
@@ -402,7 +412,7 @@
def getcommit(self, rev):
if rev not in self.commits:
- uuid, module, revnum = self.revsplit(rev)
+ uuid, module, revnum = revsplit(rev)
self.module = module
self.reparent(module)
# We assume that:
@@ -529,16 +539,6 @@
def revnum(self, rev):
return int(rev.split('@')[-1])
- def revsplit(self, rev):
- url, revnum = rev.rsplit('@', 1)
- revnum = int(revnum)
- parts = url.split('/', 1)
- uuid = parts.pop(0)[4:]
- mod = ''
- if parts:
- mod = '/' + parts[0]
- return uuid, mod, revnum
-
def latest(self, path, stop=0):
"""Find the latest revid affecting path, up to stop. It may return
a revision in a different module, since a branch may be moved without
@@ -605,7 +605,7 @@
changed, removed = set(), set()
copies = {}
- new_module, revnum = self.revsplit(rev)[1:]
+ new_module, revnum = revsplit(rev)[1:]
if new_module != self.module:
self.module = new_module
self.reparent(self.module)
@@ -622,7 +622,7 @@
continue
# Copy sources not in parent revisions cannot be
# represented, ignore their origin for now
- pmodule, prevnum = self.revsplit(parents[0])[1:]
+ pmodule, prevnum = revsplit(parents[0])[1:]
if ent.copyfrom_rev < prevnum:
continue
copyfrom_path = self.getrelpath(ent.copyfrom_path, pmodule)
@@ -633,7 +633,7 @@
copies[self.recode(entrypath)] = self.recode(copyfrom_path)
elif kind == 0: # gone, but had better be a deleted *file*
self.ui.debug("gone from %s\n" % ent.copyfrom_rev)
- pmodule, prevnum = self.revsplit(parents[0])[1:]
+ pmodule, prevnum = revsplit(parents[0])[1:]
parentpath = pmodule + "/" + entrypath
fromkind = self._checkpath(entrypath, prevnum, pmodule)
@@ -659,7 +659,7 @@
if ent.action == 'R' and parents:
# If a directory is replacing a file, mark the previous
# file as deleted
- pmodule, prevnum = self.revsplit(parents[0])[1:]
+ pmodule, prevnum = revsplit(parents[0])[1:]
pkind = self._checkpath(entrypath, prevnum, pmodule)
if pkind == svn.core.svn_node_file:
removed.add(self.recode(entrypath))
@@ -681,7 +681,7 @@
continue
# Copy sources not in parent revisions cannot be
# represented, ignore their origin for now
- pmodule, prevnum = self.revsplit(parents[0])[1:]
+ pmodule, prevnum = revsplit(parents[0])[1:]
if ent.copyfrom_rev < prevnum:
continue
copyfrompath = self.getrelpath(ent.copyfrom_path, pmodule)
@@ -736,7 +736,7 @@
# ent.copyfrom_rev may not be the actual last revision
previd = self.latest(newpath, ent.copyfrom_rev)
if previd is not None:
- prevmodule, prevnum = self.revsplit(previd)[1:]
+ prevmodule, prevnum = revsplit(previd)[1:]
if prevnum >= self.startrev:
parents = [previd]
self.ui.note(
@@ -761,9 +761,8 @@
author = author and self.recode(author) or ''
try:
branch = self.module.split("/")[-1]
- trunkname = self.ui.config('convert', 'svn.trunk', 'trunk')
- if branch == trunkname.strip('/'):
- branch = ''
+ if branch == self.trunkname:
+ branch = None
except IndexError:
branch = None
@@ -834,7 +833,7 @@
raise IOError()
mode = ''
try:
- new_module, revnum = self.revsplit(rev)[1:]
+ new_module, revnum = revsplit(rev)[1:]
if self.module != new_module:
self.module = new_module
self.reparent(self.module)
@@ -944,6 +943,7 @@
class svn_sink(converter_sink, commandline):
commit_re = re.compile(r'Committed revision (\d+).', re.M)
+ uuid_re = re.compile(r'Repository UUID:\s*(\S+)', re.M)
def prerun(self):
if self.wc:
@@ -964,8 +964,6 @@
def __init__(self, ui, path):
- if svn is None:
- raise MissingTool(_('Could not load Subversion python bindings'))
converter_sink.__init__(self, ui, path)
commandline.__init__(self, ui, 'svn')
self.delete = []
@@ -1012,8 +1010,8 @@
fp.close()
util.set_flags(hook, False, True)
- xport = transport.SvnRaTransport(url=geturl(path))
- self.uuid = svn.ra.get_uuid(xport.ra)
+ output = self.run0('info')
+ self.uuid = self.uuid_re.search(output).group(1).strip()
def wjoin(self, *names):
return os.path.join(self.wc, *names)
--- a/hgext/eol.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/eol.py Wed Mar 23 12:38:36 2011 -0500
@@ -73,11 +73,13 @@
only need to these filters until you have prepared a ``.hgeol`` file.
The ``win32text.forbid*`` hooks provided by the win32text extension
-have been unified into a single hook named ``eol.hook``. The hook will
-lookup the expected line endings from the ``.hgeol`` file, which means
-you must migrate to a ``.hgeol`` file first before using the hook.
-Remember to enable the eol extension in the repository where you
-install the hook.
+have been unified into a single hook named ``eol.checkheadshook``. The
+hook will lookup the expected line endings from the ``.hgeol`` file,
+which means you must migrate to a ``.hgeol`` file first before using
+the hook. ``eol.checkheadshook`` only checks heads, intermediate
+invalid revisions will be pushed. To forbid them completely, use the
+``eol.checkallhook`` hook. These hooks are best used as
+``pretxnchangegroup`` hooks.
See :hg:`help patterns` for more information about the glob patterns
used.
@@ -127,36 +129,119 @@
'cleverdecode:': tocrlf
}
+class eolfile(object):
+ def __init__(self, ui, root, data):
+ self._decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
+ self._encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
-def hook(ui, repo, node, hooktype, **kwargs):
- """verify that files have expected EOLs"""
+ self.cfg = config.config()
+ # Our files should not be touched. The pattern must be
+ # inserted first override a '** = native' pattern.
+ self.cfg.set('patterns', '.hg*', 'BIN')
+ # We can then parse the user's patterns.
+ self.cfg.parse('.hgeol', data)
+
+ isrepolf = self.cfg.get('repository', 'native') != 'CRLF'
+ self._encode['NATIVE'] = isrepolf and 'to-lf' or 'to-crlf'
+ iswdlf = ui.config('eol', 'native', os.linesep) in ('LF', '\n')
+ self._decode['NATIVE'] = iswdlf and 'to-lf' or 'to-crlf'
+
+ include = []
+ exclude = []
+ for pattern, style in self.cfg.items('patterns'):
+ key = style.upper()
+ if key == 'BIN':
+ exclude.append(pattern)
+ else:
+ include.append(pattern)
+ # This will match the files for which we need to care
+ # about inconsistent newlines.
+ self.match = match.match(root, '', [], include, exclude)
+
+ def setfilters(self, ui):
+ for pattern, style in self.cfg.items('patterns'):
+ key = style.upper()
+ try:
+ ui.setconfig('decode', pattern, self._decode[key])
+ ui.setconfig('encode', pattern, self._encode[key])
+ except KeyError:
+ ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
+ % (style, self.cfg.source('patterns', pattern)))
+
+ def checkrev(self, repo, ctx, files):
+ failed = []
+ for f in (files or ctx.files()):
+ if f not in ctx:
+ continue
+ for pattern, style in self.cfg.items('patterns'):
+ if not match.match(repo.root, '', [pattern])(f):
+ continue
+ target = self._encode[style.upper()]
+ data = ctx[f].data()
+ if (target == "to-lf" and "\r\n" in data
+ or target == "to-crlf" and singlelf.search(data)):
+ failed.append((str(ctx), target, f))
+ break
+ return failed
+
+def parseeol(ui, repo, nodes):
+ try:
+ for node in nodes:
+ try:
+ if node is None:
+ # Cannot use workingctx.data() since it would load
+ # and cache the filters before we configure them.
+ data = repo.wfile('.hgeol').read()
+ else:
+ data = repo[node]['.hgeol'].data()
+ return eolfile(ui, repo.root, data)
+ except (IOError, LookupError):
+ pass
+ except error.ParseError, inst:
+ ui.warn(_("warning: ignoring .hgeol file due to parse error "
+ "at %s: %s\n") % (inst.args[1], inst.args[0]))
+ return None
+
+def _checkhook(ui, repo, node, headsonly):
+ # Get revisions to check and touched files at the same time
files = set()
+ revs = set()
for rev in xrange(repo[node].rev(), len(repo)):
- files.update(repo[rev].files())
- tip = repo['tip']
- for f in files:
- if f not in tip:
- continue
- for pattern, target in ui.configitems('encode'):
- if match.match(repo.root, '', [pattern])(f):
- data = tip[f].data()
- if target == "to-lf" and "\r\n" in data:
- raise util.Abort(_("%s should not have CRLF line endings")
- % f)
- elif target == "to-crlf" and singlelf.search(data):
- raise util.Abort(_("%s should not have LF line endings")
- % f)
- # Ignore other rules for this file
- break
+ revs.add(rev)
+ if headsonly:
+ ctx = repo[rev]
+ files.update(ctx.files())
+ for pctx in ctx.parents():
+ revs.discard(pctx.rev())
+ failed = []
+ for rev in revs:
+ ctx = repo[rev]
+ eol = parseeol(ui, repo, [ctx.node()])
+ if eol:
+ failed.extend(eol.checkrev(repo, ctx, files))
+ if failed:
+ eols = {'to-lf': 'CRLF', 'to-crlf': 'LF'}
+ msgs = []
+ for node, target, f in failed:
+ msgs.append(_(" %s in %s should not have %s line endings") %
+ (f, node, eols[target]))
+ raise util.Abort(_("end-of-line check failed:\n") + "\n".join(msgs))
+
+def checkallhook(ui, repo, node, hooktype, **kwargs):
+ """verify that files have expected EOLs"""
+ _checkhook(ui, repo, node, False)
+
+def checkheadshook(ui, repo, node, hooktype, **kwargs):
+ """verify that files have expected EOLs"""
+ _checkhook(ui, repo, node, True)
+
+# "checkheadshook" used to be called "hook"
+hook = checkheadshook
def preupdate(ui, repo, hooktype, parent1, parent2):
#print "preupdate for %s: %s -> %s" % (repo.root, parent1, parent2)
- try:
- repo.readhgeol(parent1)
- except error.ParseError, inst:
- ui.warn(_("warning: ignoring .hgeol file due to parse error "
- "at %s: %s\n") % (inst.args[1], inst.args[0]))
+ repo.loadeol([parent1])
return False
def uisetup(ui):
@@ -184,66 +269,15 @@
class eolrepo(repo.__class__):
- _decode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
- _encode = {'LF': 'to-lf', 'CRLF': 'to-crlf', 'BIN': 'is-binary'}
-
- def readhgeol(self, node=None, data=None):
- if data is None:
- try:
- if node is None:
- data = self.wfile('.hgeol').read()
- else:
- data = self[node]['.hgeol'].data()
- except (IOError, LookupError):
- return None
-
- if self.ui.config('eol', 'native', os.linesep) in ('LF', '\n'):
- self._decode['NATIVE'] = 'to-lf'
- else:
- self._decode['NATIVE'] = 'to-crlf'
-
- eol = config.config()
- # Our files should not be touched. The pattern must be
- # inserted first override a '** = native' pattern.
- eol.set('patterns', '.hg*', 'BIN')
- # We can then parse the user's patterns.
- eol.parse('.hgeol', data)
-
- if eol.get('repository', 'native') == 'CRLF':
- self._encode['NATIVE'] = 'to-crlf'
- else:
- self._encode['NATIVE'] = 'to-lf'
-
- for pattern, style in eol.items('patterns'):
- key = style.upper()
- try:
- self.ui.setconfig('decode', pattern, self._decode[key])
- self.ui.setconfig('encode', pattern, self._encode[key])
- except KeyError:
- self.ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
- % (style, eol.source('patterns', pattern)))
-
- include = []
- exclude = []
- for pattern, style in eol.items('patterns'):
- key = style.upper()
- if key == 'BIN':
- exclude.append(pattern)
- else:
- include.append(pattern)
-
- # This will match the files for which we need to care
- # about inconsistent newlines.
- return match.match(self.root, '', [], include, exclude)
+ def loadeol(self, nodes):
+ eol = parseeol(self.ui, self, nodes)
+ if eol is None:
+ return None
+ eol.setfilters(self.ui)
+ return eol.match
def _hgcleardirstate(self):
- try:
- self._eolfile = self.readhgeol() or self.readhgeol('tip')
- except error.ParseError, inst:
- ui.warn(_("warning: ignoring .hgeol file due to parse error "
- "at %s: %s\n") % (inst.args[1], inst.args[0]))
- self._eolfile = None
-
+ self._eolfile = self.loadeol([None, 'tip'])
if not self._eolfile:
self._eolfile = util.never
return
--- a/hgext/extdiff.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/extdiff.py Wed Mar 23 12:38:36 2011 -0500
@@ -121,7 +121,7 @@
msg = _('cannot specify --rev and --change at the same time')
raise util.Abort(msg)
elif change:
- node2 = repo.lookup(change)
+ node2 = cmdutil.revsingle(repo, change, None).node()
node1a, node1b = repo.changelog.parents(node2)
else:
node1a, node2 = cmdutil.revpair(repo, revs)
--- a/hgext/graphlog.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/graphlog.py Wed Mar 23 12:38:36 2011 -0500
@@ -324,6 +324,7 @@
except TypeError, e:
if len(args) > wrapfn.func_code.co_argcount:
raise util.Abort(_('--graph option allows at most one file'))
+ raise
return orig(*args, **kwargs)
entry = extensions.wrapcommand(table, cmd, graph)
entry[1].append(('G', 'graph', None, _("show the revision DAG")))
--- a/hgext/keyword.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/keyword.py Wed Mar 23 12:38:36 2011 -0500
@@ -109,11 +109,26 @@
}
# date like in cvs' $Date
-utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
+def utcdate(text):
+ ''':utcdate: Date. Returns a UTC-date in this format: "2009/08/18 11:00:13".
+ '''
+ return util.datestr((text[0], 0), '%Y/%m/%d %H:%M:%S')
# date like in svn's $Date
-svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
+def svnisodate(text):
+ ''':svnisodate: Date. Returns a date in this format: "2009-08-18 13:00:13
+ +0200 (Tue, 18 Aug 2009)".
+ '''
+ return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
# date like in svn's $Id
-svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ')
+def svnutcdate(text):
+ ''':svnutcdate: Date. Returns a UTC-date in this format: "2009-08-18
+ 11:00:13Z".
+ '''
+ return util.datestr((text[0], 0), '%Y-%m-%d %H:%M:%SZ')
+
+templatefilters.filters.update({'utcdate': utcdate,
+ 'svnisodate': svnisodate,
+ 'svnutcdate': svnutcdate})
# make keyword tools accessible
kwtools = {'templater': None, 'hgcmd': ''}
@@ -176,9 +191,6 @@
for k, v in kwmaps)
else:
self.templates = _defaultkwmaps(self.ui)
- templatefilters.filters.update({'utcdate': utcdate,
- 'svnisodate': svnisodate,
- 'svnutcdate': svnutcdate})
@util.propertycache
def escape(self):
--- a/hgext/mq.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/mq.py Wed Mar 23 12:38:36 2011 -0500
@@ -1899,7 +1899,7 @@
With -g/--git, patches imported with --rev will use the git diff
format. See the diffs help topic for information on why this is
important for preserving rename/copy information and permission
- changes.
+ changes. Use :hg:`qfinish` to remove changesets from mq control.
To import a patch from standard input, pass - as the patch file.
When importing from standard input, a patch name must be specified
--- a/hgext/rebase.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/rebase.py Wed Mar 23 12:38:36 2011 -0500
@@ -90,7 +90,8 @@
contf = opts.get('continue')
abortf = opts.get('abort')
collapsef = opts.get('collapse', False)
- extrafn = opts.get('extrafn')
+ collapsemsg = cmdutil.logmessage(opts)
+ extrafn = opts.get('extrafn') # internal, used by e.g. hgsubversion
keepf = opts.get('keep', False)
keepbranchesf = opts.get('keepbranches', False)
detachf = opts.get('detach', False)
@@ -98,6 +99,10 @@
# other extensions
keepopen = opts.get('keepopen', False)
+ if collapsemsg and not collapsef:
+ raise util.Abort(
+ _('message can only be specified with collapse'))
+
if contf or abortf:
if contf and abortf:
raise util.Abort(_('cannot use both abort and continue'))
@@ -138,8 +143,7 @@
external = checkexternal(repo, state, targetancestors)
if keepbranchesf:
- if extrafn:
- raise util.Abort(_('cannot use both keepbranches and extrafn'))
+ assert not extrafn, 'cannot use both keepbranches and extrafn'
def extrafn(ctx, extra):
extra['branch'] = ctx.branch()
@@ -190,11 +194,14 @@
if collapsef and not keepopen:
p1, p2 = defineparents(repo, min(state), target,
state, targetancestors)
- commitmsg = 'Collapsed revision'
- for rebased in state:
- if rebased not in skipped and state[rebased] != nullmerge:
- commitmsg += '\n* %s' % repo[rebased].description()
- commitmsg = ui.edit(commitmsg, repo.ui.username())
+ if collapsemsg:
+ commitmsg = collapsemsg
+ else:
+ commitmsg = 'Collapsed revision'
+ for rebased in state:
+ if rebased not in skipped and state[rebased] != nullmerge:
+ commitmsg += '\n* %s' % repo[rebased].description()
+ commitmsg = ui.edit(commitmsg, repo.ui.username())
newrev = concludenode(repo, rev, p1, external, commitmsg=commitmsg,
extrafn=extrafn)
@@ -475,9 +482,10 @@
if src:
commonbase = repo[src].ancestor(repo[dest])
+ samebranch = repo[src].branch() == repo[dest].branch()
if commonbase == repo[src]:
raise util.Abort(_('source is ancestor of destination'))
- if commonbase == repo[dest]:
+ if samebranch and commonbase == repo[dest]:
raise util.Abort(_('source is descendant of destination'))
source = repo[src].rev()
if detach:
@@ -565,6 +573,10 @@
('d', 'dest', '',
_('rebase onto the specified changeset'), _('REV')),
('', 'collapse', False, _('collapse the rebased changesets')),
+ ('m', 'message', '',
+ _('use text as collapse commit message'), _('TEXT')),
+ ('l', 'logfile', '',
+ _('read collapse commit message from file'), _('FILE')),
('', 'keep', False, _('keep original changesets')),
('', 'keepbranches', False, _('keep original branch names')),
('', 'detach', False, _('force detaching of source from its original '
--- a/hgext/relink.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/relink.py Wed Mar 23 12:38:36 2011 -0500
@@ -172,8 +172,8 @@
ui.progress(_('relinking'), None)
- ui.status(_('relinked %d files (%d bytes reclaimed)\n') %
- (relinked, savedbytes))
+ ui.status(_('relinked %d files (%s reclaimed)\n') %
+ (relinked, util.bytecount(savedbytes)))
cmdtable = {
'relink': (
--- a/hgext/transplant.py Wed Mar 23 13:58:33 2011 -0300
+++ b/hgext/transplant.py Wed Mar 23 12:38:36 2011 -0500
@@ -17,7 +17,7 @@
import os, tempfile
from mercurial import bundlerepo, cmdutil, hg, merge, match
from mercurial import patch, revlog, util, error
-from mercurial import revset
+from mercurial import revset, templatekw
class transplantentry(object):
def __init__(self, lnode, rnode):
@@ -177,12 +177,11 @@
lock.release()
wlock.release()
- def filter(self, filter, changelog, patchfile):
+ def filter(self, filter, node, changelog, patchfile):
'''arbitrarily rewrite changeset before applying it'''
self.ui.status(_('filtering %s\n') % patchfile)
user, date, msg = (changelog[1], changelog[2], changelog[4])
-
fd, headerfile = tempfile.mkstemp(prefix='hg-transplant-')
fp = os.fdopen(fd, 'w')
fp.write("# HG changeset patch\n")
@@ -194,7 +193,9 @@
try:
util.system('%s %s %s' % (filter, util.shellquote(headerfile),
util.shellquote(patchfile)),
- environ={'HGUSER': changelog[1]},
+ environ={'HGUSER': changelog[1],
+ 'HGREVISION': revlog.hex(node),
+ },
onerr=util.Abort, errprefix=_('filter failed'))
user, date, msg = self.parselog(file(headerfile))[1:4]
finally:
@@ -209,7 +210,7 @@
date = "%d %d" % (time, timezone)
extra = {'transplant_source': node}
if filter:
- (user, date, message) = self.filter(filter, cl, patchfile)
+ (user, date, message) = self.filter(filter, node, cl, patchfile)
if log:
# we don't translate messages inserted into commits
@@ -607,8 +608,15 @@
cs.add(r)
return [r for r in s if r in cs]
+def kwtransplanted(repo, ctx, **args):
+ """:transplanted: String. The node identifier of the transplanted
+ changeset if any."""
+ n = ctx.extra().get('transplant_source')
+ return n and revlog.hex(n) or ''
+
def extsetup(ui):
revset.symbols['transplanted'] = revsettransplanted
+ templatekw.keywords['transplanted'] = kwtransplanted
cmdtable = {
"transplant":
@@ -632,4 +640,4 @@
}
# tell hggettext to extract docstrings from these functions:
-i18nfunctions = [revsettransplanted]
+i18nfunctions = [revsettransplanted, kwtransplanted]
--- a/mercurial/ancestor.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/ancestor.py Wed Mar 23 12:38:36 2011 -0500
@@ -9,9 +9,10 @@
def ancestor(a, b, pfunc):
"""
- return a minimal-distance ancestor of nodes a and b, or None if there is no
- such ancestor. Note that there can be several ancestors with the same
- (minimal) distance, and the one returned is arbitrary.
+ Returns the common ancestor of a and b that is furthest from a
+ root (as measured by longest path) or None if no ancestor is
+ found. If there are multiple common ancestors at the same
+ distance, the first one found is returned.
pfunc must return a list of parent vertices for a given vertex
"""
@@ -22,6 +23,7 @@
a, b = sorted([a, b])
# find depth from root of all ancestors
+ # depth is stored as a negative for heapq
parentcache = {}
visit = [a, b]
depth = {}
@@ -39,6 +41,7 @@
if p not in depth:
visit.append(p)
if visit[-1] == vertex:
+ # -(maximum distance of parents + 1)
depth[vertex] = min([depth[p] for p in pl]) - 1
visit.pop()
--- a/mercurial/archival.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/archival.py Wed Mar 23 12:38:36 2011 -0500
@@ -9,7 +9,7 @@
from node import hex
import cmdutil
import util, encoding
-import cStringIO, os, stat, tarfile, time, zipfile
+import cStringIO, os, tarfile, time, zipfile
import zlib, gzip
def tidyprefix(dest, kind, prefix):
@@ -172,10 +172,10 @@
# unzip will not honor unix file modes unless file creator is
# set to unix (id 3).
i.create_system = 3
- ftype = stat.S_IFREG
+ ftype = 0x8000 # UNX_IFREG in unzip source code
if islink:
mode = 0777
- ftype = stat.S_IFLNK
+ ftype = 0xa000 # UNX_IFLNK in unzip source code
i.external_attr = (mode | ftype) << 16L
self.z.writestr(i, data)
--- a/mercurial/bdiff.c Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/bdiff.c Wed Mar 23 12:38:36 2011 -0500
@@ -49,7 +49,7 @@
#include "util.h"
struct line {
- int h, len, n, e;
+ int hash, len, n, e;
const char *l;
};
@@ -63,9 +63,10 @@
struct hunk *next;
};
-int splitlines(const char *a, int len, struct line **lr)
+static int splitlines(const char *a, int len, struct line **lr)
{
- int h, i;
+ unsigned hash;
+ int i;
const char *p, *b = a;
const char * const plast = a + len - 1;
struct line *l;
@@ -81,14 +82,14 @@
return -1;
/* build the line array and calculate hashes */
- h = 0;
+ hash = 0;
for (p = a; p < a + len; p++) {
/* Leonid Yuriev's hash */
- h = (h * 1664525) + *p + 1013904223;
+ hash = (hash * 1664525) + (unsigned char)*p + 1013904223;
if (*p == '\n' || p == plast) {
- l->h = h;
- h = 0;
+ l->hash = hash;
+ hash = 0;
l->len = p - b + 1;
l->l = b;
l->n = INT_MAX;
@@ -98,14 +99,15 @@
}
/* set up a sentinel */
- l->h = l->len = 0;
+ l->hash = 0;
+ l->len = 0;
l->l = a + len;
return i - 1;
}
-int inline cmp(struct line *a, struct line *b)
+static inline int cmp(struct line *a, struct line *b)
{
- return a->h != b->h || a->len != b->len || memcmp(a->l, b->l, a->len);
+ return a->hash != b->hash || a->len != b->len || memcmp(a->l, b->l, a->len);
}
static int equatelines(struct line *a, int an, struct line *b, int bn)
@@ -138,7 +140,7 @@
/* add lines to the hash table chains */
for (i = bn - 1; i >= 0; i--) {
/* find the equivalence class */
- for (j = b[i].h & buckets; h[j].pos != INT_MAX;
+ for (j = b[i].hash & buckets; h[j].pos != INT_MAX;
j = (j + 1) & buckets)
if (!cmp(b + i, b + h[j].pos))
break;
@@ -156,7 +158,7 @@
/* match items in a to their equivalence class in b */
for (i = 0; i < an; i++) {
/* find the equivalence class */
- for (j = a[i].h & buckets; h[j].pos != INT_MAX;
+ for (j = a[i].hash & buckets; h[j].pos != INT_MAX;
j = (j + 1) & buckets)
if (!cmp(a + i, b + h[j].pos))
break;
--- a/mercurial/bookmarks.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/bookmarks.py Wed Mar 23 12:38:36 2011 -0500
@@ -101,13 +101,7 @@
if current == mark:
return
- refs = repo._bookmarks
-
- # do not update if we do update to a rev equal to the current bookmark
- if (mark and mark not in refs and
- current and refs[current] == repo.changectx('.').node()):
- return
- if mark not in refs:
+ if mark not in repo._bookmarks:
mark = ''
if not valid(mark):
raise util.Abort(_("bookmark '%s' contains illegal "
@@ -122,6 +116,15 @@
wlock.release()
repo._bookmarkcurrent = mark
+def updatecurrentbookmark(repo, oldnode, curbranch):
+ try:
+ update(repo, oldnode, repo.branchtags()[curbranch])
+ except KeyError:
+ if curbranch == "default": # no default branch!
+ update(repo, oldnode, repo.lookup("tip"))
+ else:
+ raise util.Abort(_("branch %s not found") % curbranch)
+
def update(repo, parents, node):
marks = repo._bookmarks
update = False
@@ -163,6 +166,28 @@
finally:
w.release()
+def updatefromremote(ui, repo, remote):
+ ui.debug("checking for updated bookmarks\n")
+ rb = remote.listkeys('bookmarks')
+ changed = False
+ for k in rb.keys():
+ if k in repo._bookmarks:
+ nr, nl = rb[k], repo._bookmarks[k]
+ if nr in repo:
+ cr = repo[nr]
+ cl = repo[nl]
+ if cl.rev() >= cr.rev():
+ continue
+ if cr in cl.descendants():
+ repo._bookmarks[k] = cr.node()
+ changed = True
+ ui.status(_("updating bookmark %s\n") % k)
+ else:
+ ui.warn(_("not updating divergent"
+ " bookmark %s\n") % k)
+ if changed:
+ write(repo)
+
def diff(ui, repo, remote):
ui.status(_("searching for changed bookmarks\n"))
--- a/mercurial/commands.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/commands.py Wed Mar 23 12:38:36 2011 -0500
@@ -5,7 +5,7 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-from node import hex, nullid, nullrev, short
+from node import hex, bin, nullid, nullrev, short
from lock import release
from i18n import _, gettext
import os, re, sys, difflib, time, tempfile
@@ -13,7 +13,7 @@
import patch, help, mdiff, url, encoding, templatekw, discovery
import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
import merge as mergemod
-import minirst, revset
+import minirst, revset, templatefilters
import dagparser
# Commands start here, listed alphabetically
@@ -126,8 +126,12 @@
lastfunc = funcmap[-1]
funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
+ def bad(x, y):
+ raise util.Abort("%s: %s" % (x, y))
+
ctx = cmdutil.revsingle(repo, opts.get('rev'))
m = cmdutil.match(repo, pats, opts)
+ m.bad = bad
follow = not opts.get('no_follow')
for abs in ctx.walk(m):
fctx = ctx[abs]
@@ -303,7 +307,8 @@
return 0
def bisect(ui, repo, rev=None, extra=None, command=None,
- reset=None, good=None, bad=None, skip=None, noupdate=None):
+ reset=None, good=None, bad=None, skip=None, extend=None,
+ noupdate=None):
"""subdivision search of changesets
This command helps to find changesets which introduce problems. To
@@ -326,6 +331,17 @@
Returns 0 on success.
"""
+ def extendbisectrange(nodes, good):
+ # bisect is incomplete when it ends on a merge node and
+ # one of the parent was not checked.
+ parents = repo[nodes[0]].parents()
+ if len(parents) > 1:
+ side = good and state['bad'] or state['good']
+ num = len(set(i.node() for i in parents) & set(side))
+ if num == 1:
+ return parents[0].ancestor(parents[1])
+ return None
+
def print_result(nodes, good):
displayer = cmdutil.show_changeset(ui, repo, {})
if len(nodes) == 1:
@@ -336,14 +352,12 @@
ui.write(_("The first bad revision is:\n"))
displayer.show(repo[nodes[0]])
parents = repo[nodes[0]].parents()
- if len(parents) > 1:
- side = good and state['bad'] or state['good']
- num = len(set(i.node() for i in parents) & set(side))
- if num == 1:
- common = parents[0].ancestor(parents[1])
- ui.write(_('Not all ancestors of this changeset have been'
- ' checked.\nTo check the other ancestors, start'
- ' from the common ancestor, %s.\n' % common))
+ extendnode = extendbisectrange(nodes, good)
+ if extendnode is not None:
+ ui.write(_('Not all ancestors of this changeset have been'
+ ' checked.\nUse bisect --extend to continue the '
+ 'bisection from\nthe common ancestor, %s.\n')
+ % short(extendnode.node()))
else:
# multiple possible revisions
if good:
@@ -376,7 +390,7 @@
bad = True
else:
reset = True
- elif extra or good + bad + skip + reset + bool(command) > 1:
+ elif extra or good + bad + skip + reset + extend + bool(command) > 1:
raise util.Abort(_('incompatible arguments'))
if reset:
@@ -440,6 +454,18 @@
# actually bisect
nodes, changesets, good = hbisect.bisect(repo.changelog, state)
+ if extend:
+ if not changesets:
+ extendnode = extendbisectrange(nodes, good)
+ if extendnode is not None:
+ ui.write(_("Extending search to changeset %d:%s\n"
+ % (extendnode.rev(), short(extendnode.node()))))
+ if noupdate:
+ return
+ cmdutil.bail_if_changed(repo)
+ return hg.clean(repo, extendnode.node())
+ raise util.Abort(_("nothing to extend"))
+
if changesets == 0:
print_result(nodes, good)
else:
@@ -1175,6 +1201,7 @@
if len(items) > 1 or items and sections:
raise util.Abort(_('only one config item permitted'))
for section, name, value in ui.walkconfig(untrusted=untrusted):
+ value = str(value).replace('\n', '\\n')
sectname = section + '.' + name
if values:
for v in values:
@@ -1191,6 +1218,58 @@
ui.configsource(section, name, untrusted))
ui.write('%s=%s\n' % (sectname, value))
+def debugknown(ui, repopath, *ids, **opts):
+ """test whether node ids are known to a repo
+
+ Every ID must be a full-length hex node id string. Returns a list of 0s and 1s
+ indicating unknown/known.
+ """
+ repo = hg.repository(ui, repopath)
+ if not repo.capable('known'):
+ raise util.Abort("known() not supported by target repository")
+ flags = repo.known([bin(s) for s in ids])
+ ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
+
+def debugbundle(ui, bundlepath, all=None, **opts):
+ """lists the contents of a bundle"""
+ f = url.open(ui, bundlepath)
+ try:
+ gen = changegroup.readbundle(f, bundlepath)
+ if all:
+ ui.write("format: id, p1, p2, cset, len(delta)\n")
+
+ def showchunks(named):
+ ui.write("\n%s\n" % named)
+ while 1:
+ chunkdata = gen.parsechunk()
+ if not chunkdata:
+ break
+ node = chunkdata['node']
+ p1 = chunkdata['p1']
+ p2 = chunkdata['p2']
+ cs = chunkdata['cs']
+ delta = chunkdata['data']
+ ui.write("%s %s %s %s %s\n" %
+ (hex(node), hex(p1), hex(p2),
+ hex(cs), len(delta)))
+
+ showchunks("changelog")
+ showchunks("manifest")
+ while 1:
+ fname = gen.chunk()
+ if not fname:
+ break
+ showchunks(fname)
+ else:
+ while 1:
+ chunkdata = gen.parsechunk()
+ if not chunkdata:
+ break
+ node = chunkdata['node']
+ ui.write("%s\n" % hex(node))
+ finally:
+ f.close()
+
def debugpushkey(ui, repopath, namespace, *keyinfo):
'''access the pushkey key/value protocol
@@ -1214,7 +1293,7 @@
def debugrevspec(ui, repo, expr):
'''parse and apply a revision specification'''
if ui.verbose:
- tree = revset.parse(expr)
+ tree = revset.parse(expr)[0]
ui.note(tree, "\n")
func = revset.match(expr)
for c in func(repo, range(len(repo))):
@@ -1555,6 +1634,21 @@
line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
ui.write("%s\n" % line.rstrip())
+def debugwireargs(ui, repopath, *vals, **opts):
+ repo = hg.repository(hg.remoteui(ui, opts), repopath)
+ for opt in remoteopts:
+ del opts[opt[1]]
+ args = {}
+ for k, v in opts.iteritems():
+ if v:
+ args[k] = v
+ # run twice to check that we don't mess up the stream for the next command
+ res1 = repo.debugwireargs(*vals, **args)
+ res2 = repo.debugwireargs(*vals, **args)
+ ui.write("%s\n" % res1)
+ if res1 != res2:
+ ui.warn("%s\n" % res2)
+
def diff(ui, repo, *pats, **opts):
"""diff repository (or selected files)
@@ -1595,7 +1689,7 @@
msg = _('cannot specify --rev and --change at the same time')
raise util.Abort(msg)
elif change:
- node2 = repo.lookup(change)
+ node2 = cmdutil.revsingle(repo, change, None).node()
node1 = repo[node2].parents()[0].node()
else:
node1, node2 = cmdutil.revpair(repo, revs)
@@ -1962,7 +2056,7 @@
Returns 0 if successful.
"""
option_lists = []
- textwidth = ui.termwidth() - 2
+ textwidth = min(ui.termwidth(), 80) - 2
def addglobalopts(aliases):
if ui.verbose:
@@ -2141,6 +2235,8 @@
'extensions\n'))
help.addtopichook('revsets', revset.makedoc)
+ help.addtopichook('templates', templatekw.makedoc)
+ help.addtopichook('templates', templatefilters.makedoc)
if name and name != 'shortlist':
i = None
@@ -2267,6 +2363,7 @@
output = []
revs = []
+ bms = []
if source:
source, branches = hg.parseurl(ui.expandpath(source))
repo = hg.repository(ui, source)
@@ -2277,10 +2374,19 @@
rev = revs[0]
if not rev:
rev = "tip"
- if num or branch or tags or bookmarks:
- raise util.Abort(_("can't query remote revision number,"
- " branch, tags, or bookmarks"))
- output = [hexfunc(repo.lookup(rev))]
+ if num or branch or tags:
+ raise util.Abort(
+ _("can't query remote revision number, branch, or tags"))
+
+ remoterev = repo.lookup(rev)
+ if default or id:
+ output = [hexfunc(remoterev)]
+
+ if 'bookmarks' in repo.listkeys('namespaces'):
+ hexremoterev = hex(remoterev)
+ bms = [bm for bm, bmrev in repo.listkeys('bookmarks').iteritems()
+ if bmrev == hexremoterev]
+
elif not rev:
ctx = repo[None]
parents = ctx.parents()
@@ -2300,6 +2406,9 @@
if num:
output.append(str(ctx.rev()))
+ if repo.local():
+ bms = ctx.bookmarks()
+
if repo.local() and default and not ui.quiet:
b = ctx.branch()
if b != 'default':
@@ -2310,8 +2419,9 @@
if t:
output.append(t)
+ if default and not ui.quiet:
# multiple bookmarks for a single parent separated by '/'
- bm = '/'.join(ctx.bookmarks())
+ bm = '/'.join(bms)
if bm:
output.append(bm)
@@ -2322,7 +2432,7 @@
output.extend(ctx.tags())
if bookmarks:
- output.extend(ctx.bookmarks())
+ output.extend(bms)
ui.write("%s\n" % ' '.join(output))
@@ -2938,6 +3048,7 @@
raise util.Abort(err)
modheads = repo.pull(other, heads=revs, force=opts.get('force'))
+ bookmarks.updatefromremote(ui, repo, other)
if checkout:
checkout = str(repo.changelog.rev(other.lookup(checkout)))
repo._subtoppath = source
@@ -3998,15 +4109,16 @@
fnames = (fname1,) + fnames
lock = repo.lock()
+ wc = repo['.']
try:
for fname in fnames:
f = url.open(ui, fname)
gen = changegroup.readbundle(f, fname)
modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname,
lock=lock)
+ bookmarks.updatecurrentbookmark(repo, wc.node(), wc.branch())
finally:
lock.release()
-
return postincoming(ui, repo, modheads, opts.get('update'), None)
def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
@@ -4053,7 +4165,7 @@
if rev and node:
raise util.Abort(_("please specify just one revision"))
- if not rev:
+ if rev is None or rev == '':
rev = node
# if we defined a bookmark, we have to remember the original bookmark name
@@ -4269,6 +4381,7 @@
('g', 'good', False, _('mark changeset good')),
('b', 'bad', False, _('mark changeset bad')),
('s', 'skip', False, _('skip testing changeset')),
+ ('e', 'extend', False, _('extend the bisect range')),
('c', 'command', '',
_('use command to check changeset state'), _('CMD')),
('U', 'noupdate', False, _('do not update to target'))],
@@ -4359,6 +4472,11 @@
('n', 'new-file', None, _('add new file at each rev')),
],
_('[OPTION]... TEXT')),
+ "debugbundle":
+ (debugbundle,
+ [('a', 'all', None, _('show all details')),
+ ],
+ _('FILE')),
"debugcheckstate": (debugcheckstate, [], ''),
"debugcommands": (debugcommands, [], _('[COMMAND]')),
"debugcomplete":
@@ -4385,6 +4503,7 @@
_('FILE')),
"debugindexdot": (debugindexdot, [], _('FILE')),
"debuginstall": (debuginstall, [], ''),
+ "debugknown": (debugknown, [], _('REPO ID...')),
"debugpushkey": (debugpushkey, [], _('REPO NAMESPACE [KEY OLD NEW]')),
"debugrebuildstate":
(debugrebuildstate,
@@ -4410,6 +4529,12 @@
_('revision to check'), _('REV'))],
_('[-r REV] [REV]')),
"debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
+ "debugwireargs":
+ (debugwireargs,
+ [('', 'three', '', 'three'),
+ ('', 'four', '', 'four'),
+ ] + remoteopts,
+ _('REPO [OPTIONS]... [ONE [TWO]]')),
"^diff":
(diff,
[('r', 'rev', [],
@@ -4743,6 +4868,7 @@
}
norepo = ("clone init version help debugcommands debugcomplete"
- " debugdate debuginstall debugfsinfo debugpushkey")
+ " debugdate debuginstall debugfsinfo debugpushkey debugwireargs"
+ " debugknown debugbundle")
optionalrepo = ("identify paths serve showconfig debugancestor debugdag"
" debugdata debugindex debugindexdot")
--- a/mercurial/config.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/config.py Wed Mar 23 12:38:36 2011 -0500
@@ -138,5 +138,5 @@
def read(self, path, fp=None, sections=None, remap=None):
if not fp:
- fp = open(path)
+ fp = util.posixfile(path)
self.parse(path, fp.read(), sections, remap, self.read)
--- a/mercurial/dirstate.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/dirstate.py Wed Mar 23 12:38:36 2011 -0500
@@ -49,6 +49,8 @@
self._rootdir = os.path.join(root, '')
self._dirty = False
self._dirtypl = False
+ self._lastnormal = set() # files believed to be normal
+ self._lastnormaltime = None
self._ui = ui
@propertycache
@@ -285,6 +287,15 @@
if f in self._copymap:
del self._copymap[f]
+ # Right now, this file is clean: but if some code in this
+ # process modifies it without changing its size before the clock
+ # ticks over to the next second, then it won't be clean anymore.
+ # So make sure that status() will look harder at it.
+ if self._lastnormaltime < s.st_mtime:
+ self._lastnormaltime = s.st_mtime
+ self._lastnormal = set()
+ self._lastnormal.add(f)
+
def normallookup(self, f):
'''Mark a file normal, but possibly dirty.'''
if self._pl[1] != nullid and f in self._map:
@@ -308,6 +319,7 @@
self._map[f] = ('n', 0, -1, -1)
if f in self._copymap:
del self._copymap[f]
+ self._lastnormal.discard(f)
def otherparent(self, f):
'''Mark as coming from the other parent, always dirty.'''
@@ -319,6 +331,7 @@
self._map[f] = ('n', 0, -2, -1)
if f in self._copymap:
del self._copymap[f]
+ self._lastnormal.discard(f)
def add(self, f):
'''Mark a file added.'''
@@ -327,6 +340,7 @@
self._map[f] = ('a', 0, -1, -1)
if f in self._copymap:
del self._copymap[f]
+ self._lastnormal.discard(f)
def remove(self, f):
'''Mark a file removed.'''
@@ -343,6 +357,7 @@
self._map[f] = ('r', 0, size, 0)
if size == 0 and f in self._copymap:
del self._copymap[f]
+ self._lastnormal.discard(f)
def merge(self, f):
'''Mark a file merged.'''
@@ -352,6 +367,7 @@
self._map[f] = ('m', s.st_mode, s.st_size, int(s.st_mtime))
if f in self._copymap:
del self._copymap[f]
+ self._lastnormal.discard(f)
def forget(self, f):
'''Forget a file.'''
@@ -361,6 +377,7 @@
del self._map[f]
except KeyError:
self._ui.warn(_("not in dirstate: %s\n") % f)
+ self._lastnormal.discard(f)
def _normalize(self, path, isknown):
normed = os.path.normcase(path)
@@ -658,6 +675,7 @@
radd = removed.append
dadd = deleted.append
cadd = clean.append
+ lastnormal = self._lastnormal.__contains__
lnkkind = stat.S_IFLNK
@@ -690,6 +708,18 @@
elif (time != int(st.st_mtime)
and (mode & lnkkind != lnkkind or self._checklink)):
ladd(fn)
+ elif lastnormal(fn):
+ # If previously in this process we recorded that
+ # this file is clean, think twice: intervening code
+ # may have modified the file in the same second
+ # without changing its size. So force caller to
+ # check file contents. Because we're not updating
+ # self._map, this only affects the current process.
+ # That should be OK because this mainly affects
+ # multiple commits in the same process, and each
+ # commit by definition makes the committed files
+ # clean.
+ ladd(fn)
elif listclean:
cadd(fn)
elif state == 'm':
--- a/mercurial/help.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/help.py Wed Mar 23 12:38:36 2011 -0500
@@ -115,3 +115,19 @@
def addtopichook(topic, rewriter):
helphooks.setdefault(topic, []).append(rewriter)
+
+def makeitemsdoc(topic, doc, marker, items):
+ """Extract docstring from the items key to function mapping, build a
+ .single documentation block and use it to overwrite the marker in doc
+ """
+ entries = []
+ for name in sorted(items):
+ text = (items[name].__doc__ or '').rstrip()
+ if not text:
+ continue
+ text = gettext(text)
+ lines = text.splitlines()
+ lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
+ entries.append('\n'.join(lines))
+ entries = '\n\n'.join(entries)
+ return doc.replace(marker, entries)
--- a/mercurial/help/templates.txt Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/help/templates.txt Wed Mar 23 12:38:36 2011 -0500
@@ -23,52 +23,7 @@
keywords depends on the exact context of the templater. These
keywords are usually available for templating a log-like command:
-:author: String. The unmodified author of the changeset.
-
-:branch: String. The name of the branch on which the changeset was
- committed.
-
-:branches: List of strings. The name of the branch on which the
- changeset was committed. Will be empty if the branch name was
- default.
-
-:children: List of strings. The children of the changeset.
-
-:date: Date information. The date when the changeset was committed.
-
-:desc: String. The text of the changeset description.
-
-:diffstat: String. Statistics of changes with the following format:
- "modified files: +added/-removed lines"
-
-:files: List of strings. All files modified, added, or removed by this
- changeset.
-
-:file_adds: List of strings. Files added by this changeset.
-
-:file_copies: List of strings. Files copied in this changeset with
- their sources.
-
-:file_copies_switch: List of strings. Like "file_copies" but displayed
- only if the --copied switch is set.
-
-:file_mods: List of strings. Files modified by this changeset.
-
-:file_dels: List of strings. Files removed by this changeset.
-
-:node: String. The changeset identification hash, as a 40 hexadecimal
- digit string.
-
-:parents: List of strings. The parents of the changeset.
-
-:rev: Integer. The repository-local changeset revision number.
-
-:tags: List of strings. Any tags associated with the changeset.
-
-:latesttag: String. Most recent global tag in the ancestors of this
- changeset.
-
-:latesttagdistance: Integer. Longest path to the latest tag.
+.. keywordsmarker
The "date" keyword does not produce human-readable output. If you
want to use a date in your output, you can use a filter to process
@@ -82,82 +37,4 @@
List of filters:
-:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
- every line except the last.
-
-:age: Date. Returns a human-readable date/time difference between the
- given date/time and the current date/time.
-
-:basename: Any text. Treats the text as a path, and returns the last
- component of the path after splitting by the path separator
- (ignoring trailing separators). For example, "foo/bar/baz" becomes
- "baz" and "foo/bar//" becomes "bar".
-
-:stripdir: Treat the text as path and strip a directory level, if
- possible. For example, "foo" and "foo/bar" becomes "foo".
-
-:date: Date. Returns a date in a Unix date format, including the
- timezone: "Mon Sep 04 15:13:13 2006 0700".
-
-:domain: Any text. Finds the first string that looks like an email
- address, and extracts just the domain component. Example: ``User
- <user@example.com>`` becomes ``example.com``.
-
-:email: Any text. Extracts the first string that looks like an email
- address. Example: ``User <user@example.com>`` becomes
- ``user@example.com``.
-
-:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
- and ">" with XML entities.
-
-:hex: Any text. Convert a binary Mercurial node identifier into
- its long hexadecimal representation.
-
-:fill68: Any text. Wraps the text to fit in 68 columns.
-
-:fill76: Any text. Wraps the text to fit in 76 columns.
-
-:firstline: Any text. Returns the first line of text.
-
-:nonempty: Any text. Returns '(none)' if the string is empty.
-
-:hgdate: Date. Returns the date as a pair of numbers: "1157407993
- 25200" (Unix timestamp, timezone offset).
-
-:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
- +0200".
-
-:isodatesec: Date. Returns the date in ISO 8601 format, including
- seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
- filter.
-
-:localdate: Date. Converts a date to local date.
-
-:obfuscate: Any text. Returns the input text rendered as a sequence of
- XML entities.
-
-:person: Any text. Returns the text before an email address.
-
-:rfc822date: Date. Returns a date using the same format used in email
- headers: "Tue, 18 Aug 2009 13:00:13 +0200".
-
-:rfc3339date: Date. Returns a date using the Internet date format
- specified in RFC 3339: "2009-08-18T13:00:13+02:00".
-
-:short: Changeset hash. Returns the short form of a changeset hash,
- i.e. a 12 hexadecimal digit string.
-
-:shortdate: Date. Returns a date like "2006-09-18".
-
-:stringify: Any type. Turns the value into text by converting values into
- text and concatenating them.
-
-:strip: Any text. Strips all leading and trailing whitespace.
-
-:tabindent: Any text. Returns the text, with every line except the
- first starting with a tab character.
-
-:urlescape: Any text. Escapes all "special" characters. For example,
- "foo bar" becomes "foo%20bar".
-
-:user: Any text. Returns the user portion of an email address.
+.. filtersmarker
--- a/mercurial/hg.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/hg.py Wed Mar 23 12:38:36 2011 -0500
@@ -9,7 +9,7 @@
from i18n import _
from lock import release
from node import hex, nullid, nullrev, short
-import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo
+import localrepo, bundlerepo, httprepo, sshrepo, statichttprepo, bookmarks
import lock, util, extensions, error, encoding, node
import cmdutil, discovery, url
import merge as mergemod
@@ -366,6 +366,21 @@
dest_repo.ui.status(_("updating to branch %s\n") % bn)
_update(dest_repo, uprev)
+ # clone all bookmarks
+ if dest_repo.local() and src_repo.capable("pushkey"):
+ rb = src_repo.listkeys('bookmarks')
+ for k, n in rb.iteritems():
+ try:
+ m = dest_repo.lookup(n)
+ dest_repo._bookmarks[k] = m
+ except:
+ pass
+ if rb:
+ bookmarks.write(dest_repo)
+ elif src_repo.local() and dest_repo.capable("pushkey"):
+ for k, n in src_repo._bookmarks.iteritems():
+ dest_repo.pushkey('bookmarks', k, '', hex(n))
+
return src_repo, dest_repo
finally:
release(src_lock, dest_lock)
--- a/mercurial/hgweb/common.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/hgweb/common.py Wed Mar 23 12:38:36 2011 -0500
@@ -73,10 +73,29 @@
def __init__(self, code, message=None, headers=[]):
if message is None:
message = _statusmessage(code)
- Exception.__init__(self, code, message)
+ Exception.__init__(self)
self.code = code
self.message = message
self.headers = headers
+ def __str__(self):
+ return self.message
+
+class continuereader(object):
+ def __init__(self, f, write):
+ self.f = f
+ self._write = write
+ self.continued = False
+
+ def read(self, amt=-1):
+ if not self.continued:
+ self.continued = True
+ self._write('HTTP/1.1 100 Continue\r\n\r\n')
+ return self.f.read(amt)
+
+ def __getattr__(self, attr):
+ if attr in ('close', 'readline', 'readlines', '__iter__'):
+ return getattr(self.f, attr)
+ raise AttributeError()
def _statusmessage(code):
from BaseHTTPServer import BaseHTTPRequestHandler
--- a/mercurial/hgweb/hgweb_mod.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/hgweb/hgweb_mod.py Wed Mar 23 12:38:36 2011 -0500
@@ -121,7 +121,11 @@
self.check_perm(req, perms[cmd])
return protocol.call(self.repo, req, cmd)
except ErrorResponse, inst:
- if cmd == 'unbundle':
+ # A client that sends unbundle without 100-continue will
+ # break if we respond early.
+ if (cmd == 'unbundle' and
+ req.env.get('HTTP_EXPECT',
+ '').lower() != '100-continue'):
req.drain()
req.respond(inst, protocol.HGTYPE)
return '0\n%s\n' % inst.message
--- a/mercurial/hgweb/hgwebdir_mod.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/hgweb/hgwebdir_mod.py Wed Mar 23 12:38:36 2011 -0500
@@ -40,9 +40,10 @@
def urlrepos(prefix, roothead, paths):
"""yield url paths and filesystem paths from a list of repo paths
- >>> list(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
+ >>> conv = lambda seq: [(v, util.pconvert(p)) for v,p in seq]
+ >>> conv(urlrepos('hg', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
[('hg/r', '/opt/r'), ('hg/r/r', '/opt/r/r'), ('hg', '/opt')]
- >>> list(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
+ >>> conv(urlrepos('', '/opt', ['/opt/r', '/opt/r/r', '/opt']))
[('r', '/opt/r'), ('r/r', '/opt/r/r'), ('', '/opt')]
"""
for path in paths:
@@ -76,7 +77,10 @@
if not os.path.exists(self.conf):
raise util.Abort(_('config file %s not found!') % self.conf)
u.readconfig(self.conf, remap=map, trust=True)
- paths = u.configitems('hgweb-paths')
+ paths = []
+ for name, ignored in u.configitems('hgweb-paths'):
+ for path in u.configlist('hgweb-paths', name):
+ paths.append((name, path))
elif isinstance(self.conf, (list, tuple)):
paths = self.conf
elif isinstance(self.conf, dict):
--- a/mercurial/hgweb/protocol.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/hgweb/protocol.py Wed Mar 23 12:38:36 2011 -0500
@@ -22,7 +22,7 @@
if k == '*':
star = {}
for key in self.req.form.keys():
- if key not in keys:
+ if key != 'cmd' and key not in keys:
star[key] = self.req.form[key][0]
data['*'] = star
else:
--- a/mercurial/hgweb/server.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/hgweb/server.py Wed Mar 23 12:38:36 2011 -0500
@@ -8,6 +8,7 @@
import os, sys, errno, urllib, BaseHTTPServer, socket, SocketServer, traceback
from mercurial import util, error
+from mercurial.hgweb import common
from mercurial.i18n import _
def _splitURI(uri):
@@ -111,6 +112,9 @@
env['SERVER_PROTOCOL'] = self.request_version
env['wsgi.version'] = (1, 0)
env['wsgi.url_scheme'] = self.url_scheme
+ if env.get('HTTP_EXPECT', '').lower() == '100-continue':
+ self.rfile = common.continuereader(self.rfile, self.wfile.write)
+
env['wsgi.input'] = self.rfile
env['wsgi.errors'] = _error_logger(self)
env['wsgi.multithread'] = isinstance(self.server,
--- a/mercurial/hgweb/wsgicgi.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/hgweb/wsgicgi.py Wed Mar 23 12:38:36 2011 -0500
@@ -10,6 +10,7 @@
import os, sys
from mercurial import util
+from mercurial.hgweb import common
def launch(application):
util.set_binary(sys.stdin)
@@ -23,7 +24,11 @@
if environ['PATH_INFO'].startswith(scriptname):
environ['PATH_INFO'] = environ['PATH_INFO'][len(scriptname):]
- environ['wsgi.input'] = sys.stdin
+ stdin = sys.stdin
+ if environ.get('HTTP_EXPECT', '').lower() == '100-continue':
+ stdin = common.continuereader(stdin, sys.stdout.write)
+
+ environ['wsgi.input'] = stdin
environ['wsgi.errors'] = sys.stderr
environ['wsgi.version'] = (1, 0)
environ['wsgi.multithread'] = False
--- a/mercurial/httprepo.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/httprepo.py Wed Mar 23 12:38:36 2011 -0500
@@ -52,10 +52,13 @@
# look up capabilities only when needed
+ def _fetchcaps(self):
+ self.caps = set(self._call('capabilities').split())
+
def get_caps(self):
if self.caps is None:
try:
- self.caps = set(self._call('capabilities').split())
+ self._fetchcaps()
except error.RepoError:
self.caps = set()
self.ui.debug('capabilities: %s\n' %
@@ -73,8 +76,7 @@
data = args.pop('data', None)
headers = args.pop('headers', {})
self.ui.debug("sending %s command\n" % cmd)
- q = {"cmd": cmd}
- q.update(args)
+ q = [('cmd', cmd)] + sorted(args.items())
qs = '?%s' % urllib.urlencode(q)
cu = "%s%s" % (self._url, qs)
req = urllib2.Request(cu, data, headers)
@@ -196,7 +198,13 @@
inst = httpsrepository(ui, path)
else:
inst = httprepository(ui, path)
- inst.between([(nullid, nullid)])
+ try:
+ # Try to do useful work when checking compatibility.
+ # Usually saves a roundtrip since we want the caps anyway.
+ inst._fetchcaps()
+ except error.RepoError:
+ # No luck, try older compatibility check.
+ inst.between([(nullid, nullid)])
return inst
except error.RepoError:
ui.note('(falling back to static-http)\n')
--- a/mercurial/localrepo.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/localrepo.py Wed Mar 23 12:38:36 2011 -0500
@@ -20,7 +20,8 @@
propertycache = util.propertycache
class localrepository(repo.repository):
- capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey'))
+ capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
+ 'known'))
supportedformats = set(('revlogv1', 'parentdelta'))
supported = supportedformats | set(('store', 'fncache', 'shared',
'dotencode'))
@@ -558,6 +559,10 @@
repo = (remote and remote.local()) and remote or self
return repo[key].branch()
+ def known(self, nodes):
+ nm = self.changelog.nodemap
+ return [(n in nm) for n in nodes]
+
def local(self):
return True
@@ -1346,27 +1351,6 @@
finally:
lock.release()
- self.ui.debug("checking for updated bookmarks\n")
- rb = remote.listkeys('bookmarks')
- changed = False
- for k in rb.keys():
- if k in self._bookmarks:
- nr, nl = rb[k], self._bookmarks[k]
- if nr in self:
- cr = self[nr]
- cl = self[nl]
- if cl.rev() >= cr.rev():
- continue
- if cr in cl.descendants():
- self._bookmarks[k] = cr.node()
- changed = True
- self.ui.status(_("updating bookmark %s\n") % k)
- else:
- self.ui.warn(_("not updating divergent"
- " bookmark %s\n") % k)
- if changed:
- bookmarks.write(self)
-
return result
def checkpush(self, force, revs):
@@ -1446,7 +1430,7 @@
for node in nodes:
self.ui.debug("%s\n" % hex(node))
- def changegroupsubset(self, bases, heads, source, extranodes=None):
+ def changegroupsubset(self, bases, heads, source):
"""Compute a changegroup consisting of all the nodes that are
descendents of any of the bases and ancestors of any of the heads.
Return a chunkbuffer object whose read() method will return
@@ -1458,54 +1442,29 @@
Another wrinkle is doing the reverse, figuring out which changeset in
the changegroup a particular filenode or manifestnode belongs to.
-
- The caller can specify some nodes that must be included in the
- changegroup using the extranodes argument. It should be a dict
- where the keys are the filenames (or 1 for the manifest), and the
- values are lists of (node, linknode) tuples, where node is a wanted
- node and linknode is the changelog node that should be transmitted as
- the linkrev.
"""
- # Set up some initial variables
- # Make it easy to refer to self.changelog
cl = self.changelog
- # Compute the list of changesets in this changegroup.
- # Some bases may turn out to be superfluous, and some heads may be
- # too. nodesbetween will return the minimal set of bases and heads
- # necessary to re-create the changegroup.
+ mf = self.manifest
+ mfs = {} # needed manifests
+ fnodes = {} # needed file nodes
+
if not bases:
bases = [nullid]
- msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
+ csets, bases, heads = cl.nodesbetween(bases, heads)
- if extranodes is None:
- # can we go through the fast path ?
- heads.sort()
- allheads = self.heads()
- allheads.sort()
- if heads == allheads:
- return self._changegroup(msng_cl_lst, source)
+ # can we go through the fast path ?
+ heads.sort()
+ if heads == sorted(self.heads()):
+ return self._changegroup(csets, source)
# slow path
self.hook('preoutgoing', throw=True, source=source)
-
- self.changegroupinfo(msng_cl_lst, source)
+ self.changegroupinfo(csets, source)
# We assume that all ancestors of bases are known
commonrevs = set(cl.ancestors(*[cl.rev(n) for n in bases]))
- # Make it easy to refer to self.manifest
- mnfst = self.manifest
- # We don't know which manifests are missing yet
- msng_mnfst_set = {}
- # Nor do we know which filenodes are missing.
- msng_filenode_set = {}
-
- # A changeset always belongs to itself, so the changenode lookup
- # function for a changenode is identity.
- def identity(x):
- return x
-
# A function generating function that sets up the initial environment
# the inner function.
def filenode_collector(changedfiles):
@@ -1516,119 +1475,65 @@
# It also remembers which changenode each filenode belongs to. It
# does this by assuming the a filenode belongs to the changenode
# the first manifest that references it belongs to.
- def collect_msng_filenodes(mnfstnode):
- r = mnfst.rev(mnfstnode)
- if mnfst.deltaparent(r) in mnfst.parentrevs(r):
- # If the previous rev is one of the parents,
- # we only need to see a diff.
- deltamf = mnfst.readdelta(mnfstnode)
- # For each line in the delta
- for f, fnode in deltamf.iteritems():
- # And if the file is in the list of files we care
- # about.
- if f in changedfiles:
- # Get the changenode this manifest belongs to
- clnode = msng_mnfst_set[mnfstnode]
- # Create the set of filenodes for the file if
- # there isn't one already.
- ndset = msng_filenode_set.setdefault(f, {})
- # And set the filenode's changelog node to the
- # manifest's if it hasn't been set already.
- ndset.setdefault(fnode, clnode)
- else:
- # Otherwise we need a full manifest.
- m = mnfst.read(mnfstnode)
- # For every file in we care about.
- for f in changedfiles:
- fnode = m.get(f, None)
- # If it's in the manifest
- if fnode is not None:
- # See comments above.
- clnode = msng_mnfst_set[mnfstnode]
- ndset = msng_filenode_set.setdefault(f, {})
- ndset.setdefault(fnode, clnode)
- return collect_msng_filenodes
+ def collect(mnode):
+ r = mf.rev(mnode)
+ clnode = mfs[mnode]
+ mdata = mf.readfast(mnode)
+ for f in changedfiles:
+ if f in mdata:
+ fnodes.setdefault(f, {}).setdefault(mdata[f], clnode)
+
+ return collect
# If we determine that a particular file or manifest node must be a
# node that the recipient of the changegroup will already have, we can
# also assume the recipient will have all the parents. This function
# prunes them from the set of missing nodes.
def prune(revlog, missingnodes):
- hasset = set()
- # If a 'missing' filenode thinks it belongs to a changenode we
- # assume the recipient must have, then the recipient must have
- # that filenode.
+ # drop any nodes that claim to be part of a cset in commonrevs
+ drop = set()
for n in missingnodes:
- clrev = revlog.linkrev(revlog.rev(n))
- if clrev in commonrevs:
- hasset.add(n)
- for n in hasset:
+ if revlog.linkrev(revlog.rev(n)) in commonrevs:
+ drop.add(n)
+ for n in drop:
missingnodes.pop(n, None)
- for r in revlog.ancestors(*[revlog.rev(n) for n in hasset]):
- missingnodes.pop(revlog.node(r), None)
-
- # Add the nodes that were explicitly requested.
- def add_extra_nodes(name, nodes):
- if not extranodes or name not in extranodes:
- return
-
- for node, linknode in extranodes[name]:
- if node not in nodes:
- nodes[node] = linknode
# Now that we have all theses utility functions to help out and
# logically divide up the task, generate the group.
def gengroup():
# The set of changed files starts empty.
changedfiles = set()
- collect = changegroup.collector(cl, msng_mnfst_set, changedfiles)
+ collect = changegroup.collector(cl, mfs, changedfiles)
# Create a changenode group generator that will call our functions
# back to lookup the owning changenode and collect information.
- group = cl.group(msng_cl_lst, identity, collect)
- for cnt, chnk in enumerate(group):
- yield chnk
+ group = cl.group(csets, lambda x: x, collect)
+ for count, chunk in enumerate(group):
+ yield chunk
# revlog.group yields three entries per node, so
# dividing by 3 gives an approximation of how many
# nodes have been processed.
- self.ui.progress(_('bundling'), cnt / 3,
+ self.ui.progress(_('bundling'), count / 3,
unit=_('changesets'))
- changecount = cnt / 3
+ changecount = count / 3
+ efiles = len(changedfiles)
self.ui.progress(_('bundling'), None)
- prune(mnfst, msng_mnfst_set)
- add_extra_nodes(1, msng_mnfst_set)
- msng_mnfst_lst = msng_mnfst_set.keys()
- # Sort the manifestnodes by revision number.
- msng_mnfst_lst.sort(key=mnfst.rev)
+ prune(mf, mfs)
# Create a generator for the manifestnodes that calls our lookup
# and data collection functions back.
- group = mnfst.group(msng_mnfst_lst,
- lambda mnode: msng_mnfst_set[mnode],
- filenode_collector(changedfiles))
- efiles = {}
- for cnt, chnk in enumerate(group):
- if cnt % 3 == 1:
- mnode = chnk[:20]
- efiles.update(mnfst.readdelta(mnode))
- yield chnk
+ group = mf.group(sorted(mfs, key=mf.rev),
+ lambda mnode: mfs[mnode],
+ filenode_collector(changedfiles))
+ for count, chunk in enumerate(group):
+ yield chunk
# see above comment for why we divide by 3
- self.ui.progress(_('bundling'), cnt / 3,
+ self.ui.progress(_('bundling'), count / 3,
unit=_('manifests'), total=changecount)
self.ui.progress(_('bundling'), None)
- efiles = len(efiles)
-
- # These are no longer needed, dereference and toss the memory for
- # them.
- msng_mnfst_lst = None
- msng_mnfst_set.clear()
- if extranodes:
- for fname in extranodes:
- if isinstance(fname, int):
- continue
- msng_filenode_set.setdefault(fname, {})
- changedfiles.add(fname)
+ mfs.clear()
+
# Go through all our files in order sorted by name.
for idx, fname in enumerate(sorted(changedfiles)):
filerevlog = self.file(fname)
@@ -1636,36 +1541,33 @@
raise util.Abort(_("empty or missing revlog for %s") % fname)
# Toss out the filenodes that the recipient isn't really
# missing.
- missingfnodes = msng_filenode_set.pop(fname, {})
+ missingfnodes = fnodes.pop(fname, {})
prune(filerevlog, missingfnodes)
- add_extra_nodes(fname, missingfnodes)
# If any filenodes are left, generate the group for them,
# otherwise don't bother.
if missingfnodes:
yield changegroup.chunkheader(len(fname))
yield fname
- # Sort the filenodes by their revision # (topological order)
- nodeiter = list(missingfnodes)
- nodeiter.sort(key=filerevlog.rev)
# Create a group generator and only pass in a changenode
# lookup function as we need to collect no information
# from filenodes.
- group = filerevlog.group(nodeiter,
- lambda fnode: missingfnodes[fnode])
- for chnk in group:
+ group = filerevlog.group(
+ sorted(missingfnodes, key=filerevlog.rev),
+ lambda fnode: missingfnodes[fnode])
+ for chunk in group:
# even though we print the same progress on
# most loop iterations, put the progress call
# here so that time estimates (if any) can be updated
self.ui.progress(
_('bundling'), idx, item=fname,
unit=_('files'), total=efiles)
- yield chnk
+ yield chunk
# Signal that no more groups are left.
yield changegroup.closechunk()
self.ui.progress(_('bundling'), None)
- if msng_cl_lst:
- self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
+ if csets:
+ self.hook('outgoing', node=hex(csets[0]), source=source)
return changegroup.unbundle10(util.chunkbuffer(gengroup()), 'UN')
@@ -1689,9 +1591,6 @@
revset = set([cl.rev(n) for n in nodes])
self.changegroupinfo(nodes, source)
- def identity(x):
- return x
-
def gennodelst(log):
for r in log:
if log.linkrev(r) in revset:
@@ -1709,28 +1608,24 @@
mmfs = {}
collect = changegroup.collector(cl, mmfs, changedfiles)
- for cnt, chnk in enumerate(cl.group(nodes, identity, collect)):
+ for count, chunk in enumerate(cl.group(nodes, lambda x: x, collect)):
# revlog.group yields three entries per node, so
# dividing by 3 gives an approximation of how many
# nodes have been processed.
- self.ui.progress(_('bundling'), cnt / 3, unit=_('changesets'))
- yield chnk
- changecount = cnt / 3
+ self.ui.progress(_('bundling'), count / 3, unit=_('changesets'))
+ yield chunk
+ efiles = len(changedfiles)
+ changecount = count / 3
self.ui.progress(_('bundling'), None)
mnfst = self.manifest
nodeiter = gennodelst(mnfst)
- efiles = {}
- for cnt, chnk in enumerate(mnfst.group(nodeiter,
+ for count, chunk in enumerate(mnfst.group(nodeiter,
lookuplinkrev_func(mnfst))):
- if cnt % 3 == 1:
- mnode = chnk[:20]
- efiles.update(mnfst.readdelta(mnode))
# see above comment for why we divide by 3
- self.ui.progress(_('bundling'), cnt / 3,
+ self.ui.progress(_('bundling'), count / 3,
unit=_('manifests'), total=changecount)
- yield chnk
- efiles = len(efiles)
+ yield chunk
self.ui.progress(_('bundling'), None)
for idx, fname in enumerate(sorted(changedfiles)):
@@ -1743,11 +1638,11 @@
yield changegroup.chunkheader(len(fname))
yield fname
lookup = lookuplinkrev_func(filerevlog)
- for chnk in filerevlog.group(nodeiter, lookup):
+ for chunk in filerevlog.group(nodeiter, lookup):
self.ui.progress(
_('bundling'), idx, item=fname,
total=efiles, unit=_('files'))
- yield chnk
+ yield chunk
self.ui.progress(_('bundling'), None)
yield changegroup.closechunk()
@@ -1915,10 +1810,6 @@
self.hook("incoming", node=hex(cl.node(i)),
source=srctype, url=url)
- # FIXME - why does this care about tip?
- if newheads == oldheads:
- bookmarks.update(self, self.dirstate.parents(), self['tip'].node())
-
# never return 0 here:
if newheads < oldheads:
return newheads - oldheads - 1
@@ -2019,6 +1910,10 @@
def listkeys(self, namespace):
return pushkey.list(self, namespace)
+ def debugwireargs(self, one, two, three=None, four=None):
+ '''used to test argument passing over the wire'''
+ return "%s %s %s %s" % (one, two, three, four)
+
# used to avoid circular references so destructors work
def aftertrans(files):
renamefiles = [tuple(t) for t in files]
--- a/mercurial/manifest.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/manifest.py Wed Mar 23 12:38:36 2011 -0500
@@ -38,6 +38,13 @@
r = self.rev(node)
return self.parse(mdiff.patchtext(self.revdiff(self.deltaparent(r), r)))
+ def readfast(self, node):
+ '''use the faster of readdelta or read'''
+ r = self.rev(node)
+ if self.deltaparent(r) in self.parentrevs(r):
+ return self.readdelta(node)
+ return self.read(node)
+
def read(self, node):
if node == revlog.nullid:
return manifestdict() # don't upset local cache
--- a/mercurial/merge.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/merge.py Wed Mar 23 12:38:36 2011 -0500
@@ -494,7 +494,6 @@
p1, p2 = pl[0], repo[node]
pa = p1.ancestor(p2)
fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
- fastforward = False
### check phase
if not overwrite and len(pl) > 1:
@@ -504,9 +503,7 @@
raise util.Abort(_("merging with a working directory ancestor"
" has no effect"))
elif pa == p1:
- if p1.branch() != p2.branch():
- fastforward = True
- else:
+ if p1.branch() == p2.branch():
raise util.Abort(_("nothing to merge (use 'hg update'"
" or check 'hg heads')"))
if not force and (wc.files() or wc.deleted()):
@@ -551,7 +548,7 @@
if not partial:
repo.dirstate.setparents(fp1, fp2)
recordupdates(repo, action, branchmerge)
- if not branchmerge and not fastforward:
+ if not branchmerge:
repo.dirstate.setbranch(p2.branch())
finally:
wlock.release()
--- a/mercurial/osutil.c Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/osutil.c Wed Mar 23 12:38:36 2011 -0500
@@ -514,6 +514,22 @@
}
#endif
+#ifdef __APPLE__
+#import <ApplicationServices/ApplicationServices.h>
+
+static PyObject *isgui(PyObject *self)
+{
+ CFDictionaryRef dict = CGSessionCopyCurrentDictionary();
+
+ if (dict != NULL) {
+ CFRelease(dict);
+ return Py_True;
+ } else {
+ return Py_False;
+ }
+}
+#endif
+
static char osutil_doc[] = "Native operating system services.";
static PyMethodDef methods[] = {
@@ -524,6 +540,12 @@
"Open a file with POSIX-like semantics.\n"
"On error, this function may raise either a WindowsError or an IOError."},
#endif
+#ifdef __APPLE__
+ {
+ "isgui", (PyCFunction)isgui, METH_NOARGS,
+ "Is a CoreGraphics session available?"
+ },
+#endif
{NULL, NULL}
};
--- a/mercurial/parser.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/parser.py Wed Mar 23 12:38:36 2011 -0500
@@ -78,7 +78,9 @@
'generate a parse tree from a message'
self._iter = self._tokenizer(message)
self._advance()
- return self._parse()
+ res = self._parse()
+ token, value, pos = self.current
+ return res, pos
def eval(self, tree):
'recursively evaluate a parse tree using node methods'
if not isinstance(tree, tuple):
--- a/mercurial/patch.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/patch.py Wed Mar 23 12:38:36 2011 -0500
@@ -488,11 +488,6 @@
cand.sort(key=lambda x: abs(x - linenum))
return cand
- def hashlines(self):
- self.hash = {}
- for x, s in enumerate(self.lines):
- self.hash.setdefault(s, []).append(x)
-
def makerejlines(self, fname):
base = os.path.basename(fname)
yield "--- %s\n+++ %s\n" % (base, base)
@@ -574,8 +569,10 @@
self.dirty = 1
return 0
- # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
- self.hashlines()
+ # ok, we couldn't match the hunk. Lets look for offsets and fuzz it
+ self.hash = {}
+ for x, s in enumerate(self.lines):
+ self.hash.setdefault(s, []).append(x)
if h.hunk[-1][0] != ' ':
# if the hunk tried to put something at the bottom of the file
# override the start line and use eof here
@@ -613,6 +610,12 @@
self.rej.append(horig)
return -1
+ def close(self):
+ if self.dirty:
+ self.writelines(self.fname, self.lines)
+ self.write_rej()
+ return len(self.rej)
+
class hunk(object):
def __init__(self, desc, num, lr, context, create=False, remove=False):
self.number = num
@@ -680,6 +683,7 @@
del self.b[-1]
self.lena -= 1
self.lenb -= 1
+ self._fixnewline(lr)
def read_context_hunk(self, lr):
self.desc = lr.readline()
@@ -782,9 +786,14 @@
self.desc = "@@ -%d,%d +%d,%d @@\n" % (self.starta, self.lena,
self.startb, self.lenb)
self.hunk[0] = self.desc
+ self._fixnewline(lr)
- def fix_newline(self):
- diffhelpers.fix_newline(self.hunk, self.a, self.b)
+ def _fixnewline(self, lr):
+ l = lr.readline()
+ if l.startswith('\ '):
+ diffhelpers.fix_newline(self.hunk, self.a, self.b)
+ else:
+ lr.push(l)
def complete(self):
return len(self.a) == self.lena and len(self.b) == self.lenb
@@ -993,7 +1002,6 @@
maps filenames to gitpatch records. Unique event.
"""
changed = {}
- current_hunk = None
afile = ""
bfile = ""
state = None
@@ -1011,11 +1019,6 @@
x = lr.readline()
if not x:
break
- if current_hunk:
- if x.startswith('\ '):
- current_hunk.fix_newline()
- yield 'hunk', current_hunk
- current_hunk = None
if (state == BFILE and ((not context and x[0] == '@') or
((context is not False) and x.startswith('***************')))):
if context is None and x.startswith('***************'):
@@ -1023,18 +1026,20 @@
gpatch = changed.get(bfile)
create = afile == '/dev/null' or gpatch and gpatch.op == 'ADD'
remove = bfile == '/dev/null' or gpatch and gpatch.op == 'DELETE'
- current_hunk = hunk(x, hunknum + 1, lr, context, create, remove)
+ h = hunk(x, hunknum + 1, lr, context, create, remove)
hunknum += 1
if emitfile:
emitfile = False
- yield 'file', (afile, bfile, current_hunk)
+ yield 'file', (afile, bfile, h)
+ yield 'hunk', h
elif state == BFILE and x.startswith('GIT binary patch'):
- current_hunk = binhunk(changed[bfile])
+ h = binhunk(changed[bfile])
hunknum += 1
if emitfile:
emitfile = False
- yield 'file', ('a/' + afile, 'b/' + bfile, current_hunk)
- current_hunk.extract(lr)
+ yield 'file', ('a/' + afile, 'b/' + bfile, h)
+ h.extract(lr)
+ yield 'hunk', h
elif x.startswith('diff --git'):
# check for git diff, scanning the whole patch file if needed
m = gitre.match(x)
@@ -1083,12 +1088,6 @@
emitfile = True
state = BFILE
hunknum = 0
- if current_hunk:
- if current_hunk.complete():
- yield 'hunk', current_hunk
- else:
- raise PatchError(_("malformed patch %s %s") % (afile,
- current_hunk.desc))
def applydiff(ui, fp, changed, strip=1, eolmode='strict'):
"""Reads a patch from fp and tries to apply it.
@@ -1114,14 +1113,6 @@
cwd = os.getcwd()
opener = util.opener(cwd)
- def closefile():
- if not current_file:
- return 0
- if current_file.dirty:
- current_file.writelines(current_file.fname, current_file.lines)
- current_file.write_rej()
- return len(current_file.rej)
-
for state, values in iterhunks(ui, fp):
if state == 'hunk':
if not current_file:
@@ -1132,7 +1123,8 @@
if ret > 0:
err = 1
elif state == 'file':
- rejects += closefile()
+ if current_file:
+ rejects += current_file.close()
afile, bfile, first_hunk = values
try:
current_file, missing = selectfile(afile, bfile,
@@ -1157,7 +1149,8 @@
else:
raise util.Abort(_('unsupported parser state: %s') % state)
- rejects += closefile()
+ if current_file:
+ rejects += current_file.close()
if rejects:
return -1
--- a/mercurial/repair.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/repair.py Wed Mar 23 12:38:36 2011 -0500
@@ -11,9 +11,9 @@
from i18n import _
import os
-def _bundle(repo, bases, heads, node, suffix, extranodes=None, compress=True):
+def _bundle(repo, bases, heads, node, suffix, compress=True):
"""create a bundle with the specified revisions as a backup"""
- cg = repo.changegroupsubset(bases, heads, 'strip', extranodes)
+ cg = repo.changegroupsubset(bases, heads, 'strip')
backupdir = repo.join("strip-backup")
if not os.path.isdir(backupdir):
os.mkdir(backupdir)
@@ -33,40 +33,26 @@
return sorted(files)
-def _collectextranodes(repo, files, link):
- """return the nodes that have to be saved before the strip"""
- def collectone(cl, revlog):
- extra = []
- startrev = count = len(revlog)
+def _collectbrokencsets(repo, files, striprev):
+ """return the changesets which will be broken by the truncation"""
+ s = set()
+ def collectone(revlog):
+ links = (revlog.linkrev(i) for i in revlog)
# find the truncation point of the revlog
- for i in xrange(count):
- lrev = revlog.linkrev(i)
- if lrev >= link:
- startrev = i + 1
+ for lrev in links:
+ if lrev >= striprev:
break
+ # see if any revision after this point has a linkrev
+ # less than striprev (those will be broken by strip)
+ for lrev in links:
+ if lrev < striprev:
+ s.add(lrev)
- # see if any revision after that point has a linkrev less than link
- # (we have to manually save these guys)
- for i in xrange(startrev, count):
- node = revlog.node(i)
- lrev = revlog.linkrev(i)
- if lrev < link:
- extra.append((node, cl.node(lrev)))
-
- return extra
+ collectone(repo.manifest)
+ for fname in files:
+ collectone(repo.file(fname))
- extranodes = {}
- cl = repo.changelog
- extra = collectone(cl, repo.manifest)
- if extra:
- extranodes[1] = extra
- for fname in files:
- f = repo.file(fname)
- extra = collectone(cl, f)
- if extra:
- extranodes[fname] = extra
-
- return extranodes
+ return s
def strip(ui, repo, node, backup="all"):
cl = repo.changelog
@@ -82,28 +68,26 @@
# the list of heads and bases of the set of interesting revisions.
# (head = revision in the set that has no descendant in the set;
# base = revision in the set that has no ancestor in the set)
- tostrip = set((striprev,))
- saveheads = set()
- savebases = []
+ tostrip = set(cl.descendants(striprev))
+ tostrip.add(striprev)
+
+ files = _collectfiles(repo, striprev)
+ saverevs = _collectbrokencsets(repo, files, striprev)
+
+ # compute heads
+ saveheads = set(saverevs)
for r in xrange(striprev + 1, len(cl)):
- parents = cl.parentrevs(r)
- if parents[0] in tostrip or parents[1] in tostrip:
- # r is a descendant of striprev
- tostrip.add(r)
- # if this is a merge and one of the parents does not descend
- # from striprev, mark that parent as a savehead.
- if parents[1] != nullrev:
- for p in parents:
- if p not in tostrip and p > striprev:
- saveheads.add(p)
- else:
- # if no parents of this revision will be stripped, mark it as
- # a savebase
- if parents[0] < striprev and parents[1] < striprev:
- savebases.append(cl.node(r))
+ if r not in tostrip:
+ saverevs.add(r)
+ saveheads.difference_update(cl.parentrevs(r))
+ saveheads.add(r)
+ saveheads = [cl.node(r) for r in saveheads]
- saveheads.difference_update(parents)
- saveheads.add(r)
+ # compute base nodes
+ if saverevs:
+ descendants = set(cl.descendants(*saverevs))
+ saverevs.difference_update(descendants)
+ savebases = [cl.node(r) for r in saverevs]
bm = repo._bookmarks
updatebm = []
@@ -112,20 +96,15 @@
if rev in tostrip:
updatebm.append(m)
- saveheads = [cl.node(r) for r in saveheads]
- files = _collectfiles(repo, striprev)
-
- extranodes = _collectextranodes(repo, files, striprev)
-
# create a changegroup for all the branches we need to keep
backupfile = None
if backup == "all":
backupfile = _bundle(repo, [node], cl.heads(), node, 'backup')
repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
- if saveheads or extranodes:
+ if saveheads or savebases:
# do not compress partial bundle if we remove it from disk later
chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
- extranodes=extranodes, compress=keeppartialbundle)
+ compress=keeppartialbundle)
mfst = repo.manifest
@@ -149,7 +128,7 @@
tr.abort()
raise
- if saveheads or extranodes:
+ if saveheads or savebases:
ui.note(_("adding branch\n"))
f = open(chgrpfile, "rb")
gen = changegroup.readbundle(f, chgrpfile)
--- a/mercurial/revset.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/revset.py Wed Mar 23 12:38:36 2011 -0500
@@ -6,10 +6,10 @@
# GNU General Public License version 2 or any later version.
import re
-import parser, util, error, discovery
+import parser, util, error, discovery, help, hbisect
import bookmarks as bookmarksmod
import match as matchmod
-from i18n import _, gettext
+from i18n import _
elements = {
"(": (20, ("group", 1, ")"), ("func", 1, ")")),
@@ -394,7 +394,7 @@
for e in c.files() + [c.user(), c.description()]:
if gr.search(e):
l.append(r)
- continue
+ break
return l
def author(repo, subset, x):
@@ -423,7 +423,7 @@
for f in repo[r].files():
if m(f):
s.append(r)
- continue
+ break
return s
def contains(repo, subset, x):
@@ -438,13 +438,12 @@
for r in subset:
if pat in repo[r]:
s.append(r)
- continue
else:
for r in subset:
for f in repo[r].manifest():
if m(f):
s.append(r)
- continue
+ break
return s
def checkstatus(repo, subset, pat, field):
@@ -466,12 +465,11 @@
if fast:
if pat in files:
s.append(r)
- continue
else:
for f in files:
if m(f):
s.append(r)
- continue
+ break
return s
def modifies(repo, subset, x):
@@ -683,12 +681,27 @@
for r in bookmarksmod.listbookmarks(repo).values()])
return [r for r in subset if r in bms]
+def bisected(repo, subset, x):
+ """``bisected(string)``
+ Changesets marked in the specified bisect state (good, bad, skip).
+ """
+ state = getstring(x, _("bisect requires a string")).lower()
+ if state not in ('good', 'bad', 'skip', 'unknown'):
+ raise ParseError(_('invalid bisect state'))
+ marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
+ l = []
+ for r in subset:
+ if r in marked:
+ l.append(r)
+ return l
+
symbols = {
"adds": adds,
"all": getall,
"ancestor": ancestor,
"ancestors": ancestors,
"author": author,
+ "bisected": bisected,
"bookmark": bookmark,
"branch": branch,
"children": children,
@@ -808,26 +821,16 @@
def match(spec):
if not spec:
raise error.ParseError(_("empty query"))
- tree = parse(spec)
+ tree, pos = parse(spec)
+ if (pos != len(spec)):
+ raise error.ParseError("invalid token", pos)
weight, tree = optimize(tree, True)
def mfunc(repo, subset):
return getset(repo, subset, tree)
return mfunc
def makedoc(topic, doc):
- """Generate and include predicates help in revsets topic."""
- predicates = []
- for name in sorted(symbols):
- text = symbols[name].__doc__
- if not text:
- continue
- text = gettext(text.rstrip())
- lines = text.splitlines()
- lines[1:] = [(' ' + l.strip()) for l in lines[1:]]
- predicates.append('\n'.join(lines))
- predicates = '\n\n'.join(predicates)
- doc = doc.replace('.. predicatesmarker', predicates)
- return doc
+ return help.makeitemsdoc(topic, doc, '.. predicatesmarker', symbols)
# tell hggettext to extract docstrings from these functions:
i18nfunctions = symbols.values()
--- a/mercurial/sshrepo.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/sshrepo.py Wed Mar 23 12:38:36 2011 -0500
@@ -119,9 +119,24 @@
def _callstream(self, cmd, **args):
self.ui.debug("sending %s command\n" % cmd)
self.pipeo.write("%s\n" % cmd)
- for k, v in sorted(args.iteritems()):
+ _func, names = wireproto.commands[cmd]
+ keys = names.split()
+ wireargs = {}
+ for k in keys:
+ if k == '*':
+ wireargs['*'] = args
+ break
+ else:
+ wireargs[k] = args[k]
+ del args[k]
+ for k, v in sorted(wireargs.iteritems()):
self.pipeo.write("%s %d\n" % (k, len(v)))
- self.pipeo.write(v)
+ if isinstance(v, dict):
+ for dk, dv in v.iteritems():
+ self.pipeo.write("%s %d\n" % (dk, len(dv)))
+ self.pipeo.write(dv)
+ else:
+ self.pipeo.write(v)
self.pipeo.flush()
return self.pipei
--- a/mercurial/sshserver.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/sshserver.py Wed Mar 23 12:38:36 2011 -0500
@@ -27,21 +27,21 @@
def getargs(self, args):
data = {}
keys = args.split()
- count = len(keys)
for n in xrange(len(keys)):
argline = self.fin.readline()[:-1]
arg, l = argline.split()
- val = self.fin.read(int(l))
if arg not in keys:
raise util.Abort("unexpected parameter %r" % arg)
if arg == '*':
star = {}
- for n in xrange(int(l)):
+ for k in xrange(int(l)):
+ argline = self.fin.readline()[:-1]
arg, l = argline.split()
val = self.fin.read(int(l))
star[arg] = val
data['*'] = star
else:
+ val = self.fin.read(int(l))
data[arg] = val
return [data[k] for k in keys]
--- a/mercurial/statichttprepo.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/statichttprepo.py Wed Mar 23 12:38:36 2011 -0500
@@ -71,7 +71,7 @@
"""return a function that opens files over http"""
p = base
def o(path, mode="r", atomictemp=None):
- if 'a' in mode or 'w' in mode:
+ if mode not in ('r', 'rb'):
raise IOError('Permission denied')
f = "/".join((p, urllib.quote(path)))
return httprangereader(f, urlopener)
--- a/mercurial/subrepo.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/subrepo.py Wed Mar 23 12:38:36 2011 -0500
@@ -8,7 +8,7 @@
import errno, os, re, xml.dom.minidom, shutil, urlparse, posixpath
import stat, subprocess, tarfile
from i18n import _
-import config, util, node, error, cmdutil
+import config, util, node, error, cmdutil, bookmarks
hg = None
nullstate = ('', '', 'empty')
@@ -441,6 +441,7 @@
% (subrelpath(self), srcurl))
other = hg.repository(self._repo.ui, srcurl)
self._repo.pull(other)
+ bookmarks.updatefromremote(self._repo.ui, self._repo, other)
def get(self, state, overwrite=False):
self._get(state)
@@ -714,6 +715,12 @@
current = None
return current
+ def _gitremote(self, remote):
+ out = self._gitcommand(['remote', 'show', '-n', remote])
+ line = out.split('\n')[1]
+ i = line.index('URL: ') + len('URL: ')
+ return line[i:]
+
def _githavelocally(self, revision):
out, code = self._gitdir(['cat-file', '-e', revision])
return code == 0
@@ -767,11 +774,14 @@
def _fetch(self, source, revision):
if self._gitmissing():
- self._ui.status(_('cloning subrepo %s\n') % self._relpath)
- self._gitnodir(['clone', self._abssource(source), self._abspath])
+ source = self._abssource(source)
+ self._ui.status(_('cloning subrepo %s from %s\n') %
+ (self._relpath, source))
+ self._gitnodir(['clone', source, self._abspath])
if self._githavelocally(revision):
return
- self._ui.status(_('pulling subrepo %s\n') % self._relpath)
+ self._ui.status(_('pulling subrepo %s from %s\n') %
+ (self._relpath, self._gitremote('origin')))
# try only origin: the originally cloned repo
self._gitcommand(['fetch'])
if not self._githavelocally(revision):
--- a/mercurial/templatefilters.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/templatefilters.py Wed Mar 23 12:38:36 2011 -0500
@@ -6,13 +6,13 @@
# GNU General Public License version 2 or any later version.
import cgi, re, os, time, urllib
-import encoding, node, util
+import encoding, node, util, help
-def stringify(thing):
- '''turn nested template iterator into string.'''
- if hasattr(thing, '__iter__') and not isinstance(thing, str):
- return "".join([stringify(t) for t in thing if t is not None])
- return str(thing)
+def addbreaks(text):
+ """:addbreaks: Any text. Add an XHTML "<br />" tag before the end of
+ every line except the last.
+ """
+ return text.replace('\n', '<br/>\n')
agescales = [("year", 3600 * 24 * 365),
("month", 3600 * 24 * 30),
@@ -23,7 +23,9 @@
("second", 1)]
def age(date):
- '''turn a (timestamp, tzoff) tuple into an age string.'''
+ """:age: Date. Returns a human-readable date/time difference between the
+ given date/time and the current date/time.
+ """
def plural(t, c):
if c == 1:
@@ -34,18 +36,65 @@
now = time.time()
then = date[0]
+ future = False
if then > now:
- return 'in the future'
-
- delta = max(1, int(now - then))
- if delta > agescales[0][1] * 2:
- return util.shortdate(date)
+ future = True
+ delta = max(1, int(then - now))
+ if delta > agescales[0][1] * 30:
+ return 'in the distant future'
+ else:
+ delta = max(1, int(now - then))
+ if delta > agescales[0][1] * 2:
+ return util.shortdate(date)
for t, s in agescales:
n = delta // s
if n >= 2 or s == 1:
+ if future:
+ return '%s from now' % fmt(t, n)
return '%s ago' % fmt(t, n)
+def basename(path):
+ """:basename: Any text. Treats the text as a path, and returns the last
+ component of the path after splitting by the path separator
+ (ignoring trailing separators). For example, "foo/bar/baz" becomes
+ "baz" and "foo/bar//" becomes "bar".
+ """
+ return os.path.basename(path)
+
+def datefilter(text):
+ """:date: Date. Returns a date in a Unix date format, including the
+ timezone: "Mon Sep 04 15:13:13 2006 0700".
+ """
+ return util.datestr(text)
+
+def domain(author):
+ """:domain: Any text. Finds the first string that looks like an email
+ address, and extracts just the domain component. Example: ``User
+ <user@example.com>`` becomes ``example.com``.
+ """
+ f = author.find('@')
+ if f == -1:
+ return ''
+ author = author[f + 1:]
+ f = author.find('>')
+ if f >= 0:
+ author = author[:f]
+ return author
+
+def email(text):
+ """:email: Any text. Extracts the first string that looks like an email
+ address. Example: ``User <user@example.com>`` becomes
+ ``user@example.com``.
+ """
+ return util.email(text)
+
+def escape(text):
+ """:escape: Any text. Replaces the special XML/XHTML characters "&", "<"
+ and ">" with XML entities.
+ """
+ return cgi.escape(text, True)
+
para_re = None
space_re = None
@@ -74,40 +123,45 @@
return "".join([space_re.sub(' ', util.wrap(para, width=width)) + rest
for para, rest in findparas()])
+def fill68(text):
+ """:fill68: Any text. Wraps the text to fit in 68 columns."""
+ return fill(text, 68)
+
+def fill76(text):
+ """:fill76: Any text. Wraps the text to fit in 76 columns."""
+ return fill(text, 76)
+
def firstline(text):
- '''return the first line of text'''
+ """:firstline: Any text. Returns the first line of text."""
try:
return text.splitlines(True)[0].rstrip('\r\n')
except IndexError:
return ''
-def nl2br(text):
- '''replace raw newlines with xhtml line breaks.'''
- return text.replace('\n', '<br/>\n')
+def hexfilter(text):
+ """:hex: Any text. Convert a binary Mercurial node identifier into
+ its long hexadecimal representation.
+ """
+ return node.hex(text)
-def obfuscate(text):
- text = unicode(text, encoding.encoding, 'replace')
- return ''.join(['&#%d;' % ord(c) for c in text])
+def hgdate(text):
+ """:hgdate: Date. Returns the date as a pair of numbers: "1157407993
+ 25200" (Unix timestamp, timezone offset).
+ """
+ return "%d %d" % text
-def domain(author):
- '''get domain of author, or empty string if none.'''
- f = author.find('@')
- if f == -1:
- return ''
- author = author[f + 1:]
- f = author.find('>')
- if f >= 0:
- author = author[:f]
- return author
+def isodate(text):
+ """:isodate: Date. Returns the date in ISO 8601 format: "2009-08-18 13:00
+ +0200".
+ """
+ return util.datestr(text, '%Y-%m-%d %H:%M %1%2')
-def person(author):
- '''get name of author, or else username.'''
- if not '@' in author:
- return author
- f = author.find('<')
- if f == -1:
- return util.shortuser(author)
- return author[:f].rstrip()
+def isodatesec(text):
+ """:isodatesec: Date. Returns the date in ISO 8601 format, including
+ seconds: "2009-08-18 13:00:13 +0200". See also the rfc3339date
+ filter.
+ """
+ return util.datestr(text, '%Y-%m-%d %H:%M:%S %1%2')
def indent(text, prefix):
'''indent each non-empty line of text after first with prefix.'''
@@ -124,38 +178,6 @@
yield '\n'
return "".join(indenter())
-def permissions(flags):
- if "l" in flags:
- return "lrwxrwxrwx"
- if "x" in flags:
- return "-rwxr-xr-x"
- return "-rw-r--r--"
-
-def xmlescape(text):
- text = (text
- .replace('&', '&')
- .replace('<', '<')
- .replace('>', '>')
- .replace('"', '"')
- .replace("'", ''')) # ' invalid in HTML
- return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
-
-def uescape(c):
- if ord(c) < 0x80:
- return c
- else:
- return '\\u%04x' % ord(c)
-
-_escapes = [
- ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
- ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
-]
-
-def jsonescape(s):
- for k, v in _escapes:
- s = s.replace(k, v)
- return ''.join(uescape(c) for c in s)
-
def json(obj):
if obj is None or obj is False or obj is True:
return {None: 'null', False: 'false', True: 'true'}[obj]
@@ -180,49 +202,163 @@
else:
raise TypeError('cannot encode type %s' % obj.__class__.__name__)
+def _uescape(c):
+ if ord(c) < 0x80:
+ return c
+ else:
+ return '\\u%04x' % ord(c)
+
+_escapes = [
+ ('\\', '\\\\'), ('"', '\\"'), ('\t', '\\t'), ('\n', '\\n'),
+ ('\r', '\\r'), ('\f', '\\f'), ('\b', '\\b'),
+]
+
+def jsonescape(s):
+ for k, v in _escapes:
+ s = s.replace(k, v)
+ return ''.join(_uescape(c) for c in s)
+
+def localdate(text):
+ """:localdate: Date. Converts a date to local date."""
+ return (text[0], util.makedate()[1])
+
+def nonempty(str):
+ """:nonempty: Any text. Returns '(none)' if the string is empty."""
+ return str or "(none)"
+
+def obfuscate(text):
+ """:obfuscate: Any text. Returns the input text rendered as a sequence of
+ XML entities.
+ """
+ text = unicode(text, encoding.encoding, 'replace')
+ return ''.join(['&#%d;' % ord(c) for c in text])
+
+def permissions(flags):
+ if "l" in flags:
+ return "lrwxrwxrwx"
+ if "x" in flags:
+ return "-rwxr-xr-x"
+ return "-rw-r--r--"
+
+def person(author):
+ """:person: Any text. Returns the text before an email address."""
+ if not '@' in author:
+ return author
+ f = author.find('<')
+ if f == -1:
+ return util.shortuser(author)
+ return author[:f].rstrip()
+
+def rfc3339date(text):
+ """:rfc3339date: Date. Returns a date using the Internet date format
+ specified in RFC 3339: "2009-08-18T13:00:13+02:00".
+ """
+ return util.datestr(text, "%Y-%m-%dT%H:%M:%S%1:%2")
+
+def rfc822date(text):
+ """:rfc822date: Date. Returns a date using the same format used in email
+ headers: "Tue, 18 Aug 2009 13:00:13 +0200".
+ """
+ return util.datestr(text, "%a, %d %b %Y %H:%M:%S %1%2")
+
+def short(text):
+ """:short: Changeset hash. Returns the short form of a changeset hash,
+ i.e. a 12 hexadecimal digit string.
+ """
+ return text[:12]
+
+def shortdate(text):
+ """:shortdate: Date. Returns a date like "2006-09-18"."""
+ return util.shortdate(text)
+
+def stringescape(text):
+ return text.encode('string_escape')
+
+def stringify(thing):
+ """:stringify: Any type. Turns the value into text by converting values into
+ text and concatenating them.
+ """
+ if hasattr(thing, '__iter__') and not isinstance(thing, str):
+ return "".join([stringify(t) for t in thing if t is not None])
+ return str(thing)
+
+def strip(text):
+ """:strip: Any text. Strips all leading and trailing whitespace."""
+ return text.strip()
+
def stripdir(text):
- '''Treat the text as path and strip a directory level, if possible.'''
+ """:stripdir: Treat the text as path and strip a directory level, if
+ possible. For example, "foo" and "foo/bar" becomes "foo".
+ """
dir = os.path.dirname(text)
if dir == "":
return os.path.basename(text)
else:
return dir
-def nonempty(str):
- return str or "(none)"
+def tabindent(text):
+ """:tabindent: Any text. Returns the text, with every line except the
+ first starting with a tab character.
+ """
+ return indent(text, '\t')
+
+def urlescape(text):
+ """:urlescape: Any text. Escapes all "special" characters. For example,
+ "foo bar" becomes "foo%20bar".
+ """
+ return urllib.quote(text)
+
+def userfilter(text):
+ """:user: Any text. Returns the user portion of an email address."""
+ return util.shortuser(text)
+
+def xmlescape(text):
+ text = (text
+ .replace('&', '&')
+ .replace('<', '<')
+ .replace('>', '>')
+ .replace('"', '"')
+ .replace("'", ''')) # ' invalid in HTML
+ return re.sub('[\x00-\x08\x0B\x0C\x0E-\x1F]', ' ', text)
filters = {
- "addbreaks": nl2br,
- "basename": os.path.basename,
- "stripdir": stripdir,
+ "addbreaks": addbreaks,
"age": age,
- "date": lambda x: util.datestr(x),
+ "basename": basename,
+ "date": datefilter,
"domain": domain,
- "email": util.email,
- "escape": lambda x: cgi.escape(x, True),
- "fill68": lambda x: fill(x, width=68),
- "fill76": lambda x: fill(x, width=76),
+ "email": email,
+ "escape": escape,
+ "fill68": fill68,
+ "fill76": fill76,
"firstline": firstline,
- "tabindent": lambda x: indent(x, '\t'),
- "hgdate": lambda x: "%d %d" % x,
- "isodate": lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2'),
- "isodatesec": lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2'),
+ "hex": hexfilter,
+ "hgdate": hgdate,
+ "isodate": isodate,
+ "isodatesec": isodatesec,
"json": json,
"jsonescape": jsonescape,
- "localdate": lambda x: (x[0], util.makedate()[1]),
+ "localdate": localdate,
"nonempty": nonempty,
"obfuscate": obfuscate,
"permissions": permissions,
"person": person,
- "rfc822date": lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2"),
- "rfc3339date": lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2"),
- "hex": node.hex,
- "short": lambda x: x[:12],
- "shortdate": util.shortdate,
+ "rfc3339date": rfc3339date,
+ "rfc822date": rfc822date,
+ "short": short,
+ "shortdate": shortdate,
+ "stringescape": stringescape,
"stringify": stringify,
- "strip": lambda x: x.strip(),
- "urlescape": lambda x: urllib.quote(x),
- "user": lambda x: util.shortuser(x),
- "stringescape": lambda x: x.encode('string_escape'),
+ "strip": strip,
+ "stripdir": stripdir,
+ "tabindent": tabindent,
+ "urlescape": urlescape,
+ "user": userfilter,
"xmlescape": xmlescape,
}
+
+def makedoc(topic, doc):
+ return help.makeitemsdoc(topic, doc, '.. filtersmarker', filters)
+
+# tell hggettext to extract docstrings from these functions:
+i18nfunctions = filters.values()
--- a/mercurial/templatekw.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/templatekw.py Wed Mar 23 12:38:36 2011 -0500
@@ -6,7 +6,7 @@
# GNU General Public License version 2 or any later version.
from node import hex
-import encoding, patch, util, error
+import encoding, patch, util, error, help
def showlist(name, values, plural=None, **args):
'''expand set of values.
@@ -143,32 +143,49 @@
def showauthor(repo, ctx, templ, **args):
+ """:author: String. The unmodified author of the changeset."""
return ctx.user()
def showbranch(**args):
+ """:branch: String. The name of the branch on which the changeset was
+ committed.
+ """
return args['ctx'].branch()
def showbranches(**args):
+ """:branches: List of strings. The name of the branch on which the
+ changeset was committed. Will be empty if the branch name was
+ default.
+ """
branch = args['ctx'].branch()
if branch != 'default':
return showlist('branch', [branch], plural='branches', **args)
def showbookmarks(**args):
+ """:bookmarks: List of strings. Any bookmarks associated with the
+ changeset.
+ """
bookmarks = args['ctx'].bookmarks()
return showlist('bookmark', bookmarks, **args)
def showchildren(**args):
+ """:children: List of strings. The children of the changeset."""
ctx = args['ctx']
childrevs = ['%d:%s' % (cctx, cctx) for cctx in ctx.children()]
return showlist('children', childrevs, **args)
def showdate(repo, ctx, templ, **args):
+ """:date: Date information. The date when the changeset was committed."""
return ctx.date()
def showdescription(repo, ctx, templ, **args):
+ """:desc: String. The text of the changeset description."""
return ctx.description().strip()
def showdiffstat(repo, ctx, templ, **args):
+ """:diffstat: String. Statistics of changes with the following format:
+ "modified files: +added/-removed lines"
+ """
files, adds, removes = 0, 0, 0
for i in patch.diffstatdata(util.iterlines(ctx.diff())):
files += 1
@@ -184,10 +201,14 @@
yield templ('extra', **args)
def showfileadds(**args):
+ """:file_adds: List of strings. Files added by this changeset."""
repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
return showlist('file_add', getfiles(repo, ctx, revcache)[1], **args)
def showfilecopies(**args):
+ """:file_copies: List of strings. Files copied in this changeset with
+ their sources.
+ """
cache, ctx = args['cache'], args['ctx']
copies = args['revcache'].get('copies')
if copies is None:
@@ -207,25 +228,37 @@
# provided before calling the templater, usually with a --copies
# command line switch.
def showfilecopiesswitch(**args):
+ """:file_copies_switch: List of strings. Like "file_copies" but displayed
+ only if the --copied switch is set.
+ """
copies = args['revcache'].get('copies') or []
c = [{'name': x[0], 'source': x[1]} for x in copies]
return showlist('file_copy', c, plural='file_copies', **args)
def showfiledels(**args):
+ """:file_dels: List of strings. Files removed by this changeset."""
repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
return showlist('file_del', getfiles(repo, ctx, revcache)[2], **args)
def showfilemods(**args):
+ """:file_mods: List of strings. Files modified by this changeset."""
repo, ctx, revcache = args['repo'], args['ctx'], args['revcache']
return showlist('file_mod', getfiles(repo, ctx, revcache)[0], **args)
def showfiles(**args):
+ """:files: List of strings. All files modified, added, or removed by this
+ changeset.
+ """
return showlist('file', args['ctx'].files(), **args)
def showlatesttag(repo, ctx, templ, cache, **args):
+ """:latesttag: String. Most recent global tag in the ancestors of this
+ changeset.
+ """
return getlatesttags(repo, ctx, cache)[2]
def showlatesttagdistance(repo, ctx, templ, cache, **args):
+ """:latesttagdistance: Integer. Longest path to the latest tag."""
return getlatesttags(repo, ctx, cache)[1]
def showmanifest(**args):
@@ -236,12 +269,17 @@
return templ('manifest', **args)
def shownode(repo, ctx, templ, **args):
+ """:node: String. The changeset identification hash, as a 40 hexadecimal
+ digit string.
+ """
return ctx.hex()
def showrev(repo, ctx, templ, **args):
+ """:rev: Integer. The repository-local changeset revision number."""
return ctx.rev()
def showtags(**args):
+ """:tags: List of strings. Any tags associated with the changeset."""
return showlist('tag', args['ctx'].tags(), **args)
# keywords are callables like:
@@ -276,3 +314,8 @@
'tags': showtags,
}
+def makedoc(topic, doc):
+ return help.makeitemsdoc(topic, doc, '.. keywordsmarker', keywords)
+
+# tell hggettext to extract docstrings from these functions:
+i18nfunctions = keywords.values()
--- a/mercurial/templater.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/templater.py Wed Mar 23 12:38:36 2011 -0500
@@ -69,7 +69,6 @@
else:
raise error.ParseError(_("syntax error"), pos)
pos += 1
- data[2] = pos
yield ('end', None, pos)
def compiletemplate(tmpl, context):
@@ -91,8 +90,8 @@
parsed.append(("string", tmpl[pos:n]))
pd = [tmpl, n + 1, stop]
- parsed.append(p.parse(pd))
- pos = pd[2]
+ parseres, pos = p.parse(pd)
+ parsed.append(parseres)
return [compileexp(e, context) for e in parsed]
--- a/mercurial/ui.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/ui.py Wed Mar 23 12:38:36 2011 -0500
@@ -273,7 +273,7 @@
cfg = self._data(untrusted)
for section in cfg.sections():
for name, value in self.configitems(section, untrusted):
- yield section, name, str(value).replace('\n', '\\n')
+ yield section, name, value
def plain(self):
'''is plain mode active?
--- a/mercurial/util.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/util.py Wed Mar 23 12:38:36 2011 -0500
@@ -769,7 +769,18 @@
def gui():
'''Are we running in a GUI?'''
- return os.name == "nt" or os.name == "mac" or os.environ.get("DISPLAY")
+ if sys.platform == 'darwin':
+ if 'SSH_CONNECTION' in os.environ:
+ # handle SSH access to a box where the user is logged in
+ return False
+ elif getattr(osutil, 'isgui', None):
+ # check if a CoreGraphics session is available
+ return osutil.isgui()
+ else:
+ # pure build; use a safe default
+ return True
+ else:
+ return os.name == "nt" or os.environ.get("DISPLAY")
def mktempcopy(name, emptyok=False, createmode=None):
"""Create a temporary file with the same contents from name
--- a/mercurial/wireproto.py Wed Mar 23 13:58:33 2011 -0300
+++ b/mercurial/wireproto.py Wed Mar 23 12:38:36 2011 -0500
@@ -15,7 +15,9 @@
# list of nodes encoding / decoding
def decodelist(l, sep=' '):
- return map(bin, l.split(sep))
+ if l:
+ return map(bin, l.split(sep))
+ return []
def encodelist(l, sep=' '):
return sep.join(map(hex, l))
@@ -35,7 +37,15 @@
d = self._call("heads")
try:
return decodelist(d[:-1])
- except:
+ except ValueError:
+ self._abort(error.ResponseError(_("unexpected response:"), d))
+
+ def known(self, nodes):
+ n = encodelist(nodes)
+ d = self._call("known", nodes=n)
+ try:
+ return [bool(int(f)) for f in d]
+ except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
def branchmap(self):
@@ -57,7 +67,7 @@
try:
br = [tuple(decodelist(b)) for b in d.splitlines()]
return br
- except:
+ except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
def between(self, pairs):
@@ -68,7 +78,7 @@
d = self._call("between", pairs=n)
try:
r.extend(l and decodelist(l) or [] for l in d.splitlines())
- except:
+ except ValueError:
self._abort(error.ResponseError(_("unexpected response:"), d))
return r
@@ -133,6 +143,15 @@
self.ui.status(_('remote: '), l)
return ret
+ def debugwireargs(self, one, two, three=None, four=None):
+ # don't pass optional arguments left at their default value
+ opts = {}
+ if three is not None:
+ opts['three'] = three
+ if four is not None:
+ opts['four'] = four
+ return self._call('debugwireargs', one=one, two=two, **opts)
+
# server side
class streamres(object):
@@ -152,6 +171,17 @@
args = proto.getargs(spec)
return func(repo, proto, *args)
+def options(cmd, keys, others):
+ opts = {}
+ for k in keys:
+ if k in others:
+ opts[k] = others[k]
+ del others[k]
+ if others:
+ sys.stderr.write("abort: %s got unexpected arguments %s\n"
+ % (cmd, ",".join(others)))
+ return opts
+
def between(repo, proto, pairs):
pairs = [decodelist(p, '-') for p in pairs.split(" ")]
r = []
@@ -176,7 +206,7 @@
return "".join(r)
def capabilities(repo, proto):
- caps = 'lookup changegroupsubset branchmap pushkey'.split()
+ caps = 'lookup changegroupsubset branchmap pushkey known'.split()
if _allowstream(repo.ui):
requiredformats = repo.requirements & repo.supportedformats
# if our local revlogs are just revlogv1, add 'stream' cap
@@ -199,6 +229,11 @@
cg = repo.changegroupsubset(bases, heads, 'serve')
return streamres(proto.groupchunks(cg))
+def debugwireargs(repo, proto, one, two, others):
+ # only accept optional args from the known set
+ opts = options('debugwireargs', ['three', 'four'], others)
+ return repo.debugwireargs(one, two, **opts)
+
def heads(repo, proto):
h = repo.heads()
return encodelist(h) + "\n"
@@ -228,6 +263,9 @@
success = 0
return "%s %s\n" % (success, r)
+def known(repo, proto, nodes):
+ return ''.join(b and "1" or "0" for b in repo.known(decodelist(nodes)))
+
def pushkey(repo, proto, namespace, key, old, new):
# compatibility with pre-1.8 clients which were accidentally
# sending raw binary nodes rather than utf-8-encoded hex
@@ -343,8 +381,10 @@
'capabilities': (capabilities, ''),
'changegroup': (changegroup, 'roots'),
'changegroupsubset': (changegroupsubset, 'bases heads'),
+ 'debugwireargs': (debugwireargs, 'one two *'),
'heads': (heads, ''),
'hello': (hello, ''),
+ 'known': (known, 'nodes'),
'listkeys': (listkeys, 'namespace'),
'lookup': (lookup, 'key'),
'pushkey': (pushkey, 'namespace key old new'),
--- a/setup.py Wed Mar 23 13:58:33 2011 -0300
+++ b/setup.py Wed Mar 23 12:38:36 2011 -0500
@@ -98,24 +98,8 @@
try:
import py2exe
py2exeloaded = True
-
- # Help py2exe to find win32com.shell
- try:
- import modulefinder
- import win32com
- for p in win32com.__path__[1:]: # Take the path to win32comext
- modulefinder.AddPackagePath("win32com", p)
- pn = "win32com.shell"
- __import__(pn)
- m = sys.modules[pn]
- for p in m.__path__[1:]:
- modulefinder.AddPackagePath(pn, p)
- except ImportError:
- pass
-
except ImportError:
py2exeloaded = False
- pass
def runcmd(cmd, env):
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
@@ -330,11 +314,17 @@
Extension('mercurial.parsers', ['mercurial/parsers.c']),
]
+osutil_ldflags = []
+
+if sys.platform == 'darwin':
+ osutil_ldflags += ['-framework', 'ApplicationServices']
+
# disable osutil.c under windows + python 2.4 (issue1364)
if sys.platform == 'win32' and sys.version_info < (2, 5, 0, 'final'):
pymodules.append('mercurial.pure.osutil')
else:
- extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c']))
+ extmodules.append(Extension('mercurial.osutil', ['mercurial/osutil.c'],
+ extra_link_args=osutil_ldflags))
if sys.platform == 'linux2' and os.uname()[2] > '2.6':
# The inotify extension is only usable with Linux 2.6 kernels.
--- a/tests/run-tests.py Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/run-tests.py Wed Mar 23 12:38:36 2011 -0500
@@ -227,8 +227,8 @@
continue
for line in f.readlines():
- line = line.strip()
- if line and not line.startswith('#'):
+ line = line.split('#', 1)[0].strip()
+ if line:
blacklist[line] = filename
f.close()
--- a/tests/test-annotate.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-annotate.t Wed Mar 23 12:38:36 2011 -0500
@@ -228,3 +228,8 @@
$ hg annotate --follow foo
foo: foo
+missing file
+
+ $ hg ann nosuchfile
+ abort: nosuchfile: no such file in rev c8abddb41a00
+ [255]
--- a/tests/test-basic.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-basic.t Wed Mar 23 12:38:36 2011 -0500
@@ -20,6 +20,22 @@
summary: test
+Verify that updating to revision 0 via commands.update() works properly
+
+ $ cat <<EOF > update_to_rev0.py
+ > from mercurial import ui, hg, commands
+ > myui = ui.ui()
+ > repo = hg.repository(myui, path='.')
+ > commands.update(myui, repo, rev=0)
+ > EOF
+ $ hg up null
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ python ./update_to_rev0.py
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg identify -n
+ 0
+
+
Poke around at hashes:
$ hg manifest --debug
--- a/tests/test-bisect.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-bisect.t Wed Mar 23 12:38:36 2011 -0500
@@ -377,6 +377,44 @@
date: Thu Jan 01 00:00:06 1970 +0000
summary: msg 6
+ $ hg log -r "bisected(good)"
+ changeset: 0:b99c7b9c8e11
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: msg 0
+
+ changeset: 5:7874a09ea728
+ user: test
+ date: Thu Jan 01 00:00:05 1970 +0000
+ summary: msg 5
+
+ $ hg log -r "bisected(bad)"
+ changeset: 6:a3d5c6fdf0d3
+ user: test
+ date: Thu Jan 01 00:00:06 1970 +0000
+ summary: msg 6
+
+ $ hg log -r "bisected(skip)"
+ changeset: 1:5cd978ea5149
+ user: test
+ date: Thu Jan 01 00:00:01 1970 +0000
+ summary: msg 1
+
+ changeset: 2:db07c04beaca
+ user: test
+ date: Thu Jan 01 00:00:02 1970 +0000
+ summary: msg 2
+
+ changeset: 3:b53bea5e2fcb
+ user: test
+ date: Thu Jan 01 00:00:03 1970 +0000
+ summary: msg 3
+
+ changeset: 4:9b2ba8336a65
+ user: test
+ date: Thu Jan 01 00:00:04 1970 +0000
+ summary: msg 4
+
$ set +e
--- a/tests/test-bisect2.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-bisect2.t Wed Mar 23 12:38:36 2011 -0500
@@ -416,10 +416,14 @@
summary: merge 10,13
Not all ancestors of this changeset have been checked.
- To check the other ancestors, start from the common ancestor, dab8161ac8fc.
- $ hg bisect -g 8 # dab8161ac8fc
+ Use bisect --extend to continue the bisection from
+ the common ancestor, dab8161ac8fc.
+ $ hg bisect --extend
+ Extending search to changeset 8:dab8161ac8fc
+ 2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg bisect -g # dab8161ac8fc
Testing changeset 9:3c77083deb4a (3 changesets remaining, ~1 tests)
- 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg bisect -b
The first bad revision is:
changeset: 9:3c77083deb4a
--- a/tests/test-bookmarks-pushpull.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-bookmarks-pushpull.t Wed Mar 23 12:38:36 2011 -0500
@@ -26,6 +26,7 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
+ updating bookmark Y
(run 'hg update' to get a working copy)
$ hg bookmarks
Y 0:4e3505fd9583
@@ -176,5 +177,19 @@
no changes found
not updating divergent bookmark X
importing bookmark Z
+ $ hg clone http://localhost:$HGPORT/ cloned-bookmarks
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files (+1 heads)
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R cloned-bookmarks bookmarks
+ X 1:9b140be10808
+ Y 0:4e3505fd9583
+ Z 2:0d2164f0ce0d
+ foo -1:000000000000
+ foobar -1:000000000000
$ kill `cat ../hg.pid`
--- a/tests/test-bookmarks.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-bookmarks.t Wed Mar 23 12:38:36 2011 -0500
@@ -244,3 +244,80 @@
$ hg id
db815d6d32e6 tip Y/Z/x y
+
+test clone
+
+ $ hg bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ * Z 2:db815d6d32e6
+ x y 2:db815d6d32e6
+ $ hg clone . cloned-bookmarks
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R cloned-bookmarks bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ Z 2:db815d6d32e6
+ x y 2:db815d6d32e6
+
+test clone with pull protocol
+
+ $ hg clone --pull . cloned-bookmarks-pull
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 3 files (+1 heads)
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R cloned-bookmarks-pull bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ Z 2:db815d6d32e6
+ x y 2:db815d6d32e6
+
+test clone with a specific revision
+
+ $ hg clone -r 925d80 . cloned-bookmarks-rev
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R cloned-bookmarks-rev bookmarks
+ X2 1:925d80f479bb
+
+create bundle with two heads
+
+ $ hg clone . tobundle
+ updating to branch default
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ echo x > tobundle/x
+ $ hg -R tobundle add tobundle/x
+ $ hg -R tobundle commit -m'x'
+ $ hg -R tobundle update -r -2
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo y > tobundle/y
+ $ hg -R tobundle branch test
+ marked working directory as branch test
+ $ hg -R tobundle add tobundle/y
+ $ hg -R tobundle commit -m'y'
+ $ hg -R tobundle bundle tobundle.hg
+ searching for changes
+ 2 changesets found
+ $ hg unbundle tobundle.hg
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ (run 'hg heads' to see heads, 'hg merge' to merge)
+ $ hg update
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ * Z 3:125c9a1d6df6
+ x y 2:db815d6d32e6
+
--- a/tests/test-command-template.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-command-template.t Wed Mar 23 12:38:36 2011 -0500
@@ -1115,7 +1115,7 @@
$ hg log --template '{date|age}\n' > /dev/null || exit 1
$ hg log -l1 --template '{date|age}\n'
- in the future
+ 8 years from now
$ hg log --template '{date|date}\n'
Wed Jan 01 10:01:00 2020 +0000
Mon Jan 12 13:46:40 1970 +0000
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-commit-multiple.t Wed Mar 23 12:38:36 2011 -0500
@@ -0,0 +1,119 @@
+# reproduce issue2264, issue2516
+
+create test repo
+ $ cat <<EOF >> $HGRCPATH
+ > [extensions]
+ > transplant =
+ > graphlog =
+ > EOF
+ $ hg init repo
+ $ cd repo
+ $ template="{rev} {desc|firstline} [{branch}]\n"
+
+# we need to start out with two changesets on the default branch
+# in order to avoid the cute little optimization where transplant
+# pulls rather than transplants
+add initial changesets
+ $ echo feature1 > file1
+ $ hg ci -Am"feature 1"
+ adding file1
+ $ echo feature2 >> file2
+ $ hg ci -Am"feature 2"
+ adding file2
+
+# The changes to 'bugfix' are enough to show the bug: in fact, with only
+# those changes, it's a very noisy crash ("RuntimeError: nothing
+# committed after transplant"). But if we modify a second file in the
+# transplanted changesets, the bug is much more subtle: transplant
+# silently drops the second change to 'bugfix' on the floor, and we only
+# see it when we run 'hg status' after transplanting. Subtle data loss
+# bugs are worse than crashes, so reproduce the subtle case here.
+commit bug fixes on bug fix branch
+ $ hg branch fixes
+ marked working directory as branch fixes
+ $ echo fix1 > bugfix
+ $ echo fix1 >> file1
+ $ hg ci -Am"fix 1"
+ adding bugfix
+ $ echo fix2 > bugfix
+ $ echo fix2 >> file1
+ $ hg ci -Am"fix 2"
+ $ hg glog --template="$template"
+ @ 3 fix 2 [fixes]
+ |
+ o 2 fix 1 [fixes]
+ |
+ o 1 feature 2 [default]
+ |
+ o 0 feature 1 [default]
+
+transplant bug fixes onto release branch
+ $ hg update 0
+ 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg branch release
+ marked working directory as branch release
+ $ hg transplant 2 3
+ applying [0-9a-f]{12} (re)
+ [0-9a-f]{12} transplanted to [0-9a-f]{12} (re)
+ applying [0-9a-f]{12} (re)
+ [0-9a-f]{12} transplanted to [0-9a-f]{12} (re)
+ $ hg glog --template="$template"
+ @ 5 fix 2 [release]
+ |
+ o 4 fix 1 [release]
+ |
+ | o 3 fix 2 [fixes]
+ | |
+ | o 2 fix 1 [fixes]
+ | |
+ | o 1 feature 2 [default]
+ |/
+ o 0 feature 1 [default]
+
+ $ hg status
+ $ hg status --rev 0:4
+ M file1
+ A bugfix
+ $ hg status --rev 4:5
+ M bugfix
+ M file1
+
+now test that we fixed the bug for all scripts/extensions
+ $ cat > $TESTTMP/committwice.py <<__EOF__
+ > from mercurial import ui, hg, match, node
+ >
+ > def replacebyte(fn, b):
+ > f = open("file1", "rb+")
+ > f.seek(0, 0)
+ > f.write(b)
+ > f.close()
+ >
+ > repo = hg.repository(ui.ui(), '.')
+ > assert len(repo) == 6, \
+ > "initial: len(repo) == %d, expected 6" % len(repo)
+ > try:
+ > wlock = repo.wlock()
+ > lock = repo.lock()
+ > m = match.exact(repo.root, '', ['file1'])
+ > replacebyte("file1", "x")
+ > n = repo.commit(text="x", user="test", date=(0, 0), match=m)
+ > print "commit 1: len(repo) == %d" % len(repo)
+ > replacebyte("file1", "y")
+ > n = repo.commit(text="y", user="test", date=(0, 0), match=m)
+ > print "commit 2: len(repo) == %d" % len(repo)
+ > finally:
+ > lock.release()
+ > wlock.release()
+ > __EOF__
+ $ $PYTHON $TESTTMP/committwice.py
+ commit 1: len(repo) == 7
+ commit 2: len(repo) == 8
+
+Do a size-preserving modification outside of that process
+ $ echo abcd > bugfix
+ $ hg status
+ M bugfix
+ $ hg log --template "{rev} {desc} {files}\n" -r5:
+ 5 fix 2 bugfix file1
+ 6 x file1
+ 7 y file1
--- a/tests/test-convert-hg-startrev.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-convert-hg-startrev.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,5 +1,5 @@
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> graphlog =
> convert =
--- a/tests/test-convert-svn-branches.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-convert-svn-branches.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,7 +1,7 @@
$ "$TESTDIR/hghave" svn svn-bindings || exit 80
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
@@ -32,6 +32,21 @@
1 move back to old
0 last change to a
+Test template keywords
+
+ $ hg -R A-hg log --template '{rev} {svnuuid}{svnpath}@{svnrev}\n'
+ 10 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@10
+ 9 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@9
+ 8 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old2@8
+ 7 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@7
+ 6 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@6
+ 5 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@6
+ 4 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@5
+ 3 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@4
+ 2 644ede6c-2b81-4367-9dc8-d786514f2cde/branches/old@3
+ 1 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@2
+ 0 644ede6c-2b81-4367-9dc8-d786514f2cde/trunk@1
+
Convert again
$ hg convert --branchmap=branchmap --datesort svn-repo A-hg
--- a/tests/test-convert-svn-encoding.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-convert-svn-encoding.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,7 +1,7 @@
$ "$TESTDIR/hghave" svn svn-bindings || exit 80
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-move.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-convert-svn-move.t Wed Mar 23 12:38:36 2011 -0500
@@ -5,7 +5,7 @@
> {
> tr '\\' /
> }
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-sink.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-convert-svn-sink.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,5 +1,5 @@
- $ "$TESTDIR/hghave" svn svn-bindings no-outer-repo || exit 80
+ $ "$TESTDIR/hghave" svn no-outer-repo || exit 80
$ fixpath()
> {
@@ -22,7 +22,7 @@
> )
> }
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-source.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-convert-svn-source.t Wed Mar 23 12:38:36 2011 -0500
@@ -5,7 +5,7 @@
> {
> tr '\\' /
> }
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-startrev.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-convert-svn-startrev.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,7 +1,7 @@
$ "$TESTDIR/hghave" svn svn-bindings || exit 80
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- a/tests/test-convert-svn-tags.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-convert-svn-tags.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,7 +1,7 @@
$ "$TESTDIR/hghave" svn svn-bindings || exit 80
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> convert =
> graphlog =
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-debugbundle.t Wed Mar 23 12:38:36 2011 -0500
@@ -0,0 +1,36 @@
+
+Create a test repository:
+
+ $ hg init repo
+ $ cd repo
+ $ touch a ; hg add a ; hg ci -ma
+ $ touch b ; hg add b ; hg ci -mb
+ $ touch c ; hg add c ; hg ci -mc
+ $ hg bundle --base 0 --rev tip bundle.hg
+ 2 changesets found
+
+Terse output:
+
+ $ hg debugbundle bundle.hg
+ 0e067c57feba1a5694ca4844f05588bb1bf82342
+ 991a3460af53952d10ec8a295d3d2cc2e5fa9690
+
+Verbose output:
+
+ $ hg debugbundle --all bundle.hg
+ format: id, p1, p2, cset, len(delta)
+
+ changelog
+ 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 80
+ 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 80
+
+ manifest
+ 686dbf0aeca417636fa26a9121c681eabbb15a20 8515d4bfda768e04af4c13a69a72e28c7effbea7 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 55
+ ae25a31b30b3490a981e7b96a3238cc69583fda1 686dbf0aeca417636fa26a9121c681eabbb15a20 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 55
+
+ b
+ b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 0e067c57feba1a5694ca4844f05588bb1bf82342 12
+
+ c
+ b80de5d138758541c5f05265ad144ab9fa86d1db 0000000000000000000000000000000000000000 0000000000000000000000000000000000000000 991a3460af53952d10ec8a295d3d2cc2e5fa9690 12
+
--- a/tests/test-debugcomplete.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-debugcomplete.t Wed Mar 23 12:38:36 2011 -0500
@@ -67,6 +67,7 @@
$ hg debugcomplete debug
debugancestor
debugbuilddag
+ debugbundle
debugcheckstate
debugcommands
debugcomplete
@@ -79,6 +80,7 @@
debugindex
debugindexdot
debuginstall
+ debugknown
debugpushkey
debugrebuildstate
debugrename
@@ -87,6 +89,7 @@
debugstate
debugsub
debugwalk
+ debugwireargs
Do not show the alias of a debug command if there are other candidates
(this should hide rawcommit)
@@ -199,7 +202,7 @@
addremove: similarity, include, exclude, dry-run
archive: no-decode, prefix, rev, type, subrepos, include, exclude
backout: merge, parent, tool, rev, include, exclude, message, logfile, date, user
- bisect: reset, good, bad, skip, command, noupdate
+ bisect: reset, good, bad, skip, extend, command, noupdate
bookmarks: force, rev, delete, rename
branch: force, clean
branches: active, closed
@@ -208,6 +211,7 @@
copy: after, force, include, exclude, dry-run
debugancestor:
debugbuilddag: mergeable-file, appended-file, overwritten-file, new-file
+ debugbundle: all
debugcheckstate:
debugcommands:
debugcomplete: options
@@ -219,6 +223,7 @@
debugindex: format
debugindexdot:
debuginstall:
+ debugknown:
debugpushkey:
debugrebuildstate: rev
debugrename: rev
@@ -227,6 +232,7 @@
debugstate: nodates
debugsub: rev
debugwalk: include, exclude
+ debugwireargs: three, four, ssh, remotecmd, insecure
grep: print0, all, follow, ignore-case, files-with-matches, line-number, rev, user, date, include, exclude
heads: rev, topo, active, closed, style, template
help:
--- a/tests/test-eol-add.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-eol-add.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,6 +1,6 @@
Test adding .hgeol
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [diff]
> git = 1
> EOF
--- a/tests/test-eol-clone.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-eol-clone.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,9 +1,6 @@
Testing cloning with the EOL extension
- $ cat > $HGRCPATH <<EOF
- > [diff]
- > git = True
- >
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> eol =
>
--- a/tests/test-eol-hook.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-eol-hook.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,14 +1,7 @@
Test the EOL hook
- $ cat > $HGRCPATH <<EOF
- > [diff]
- > git = True
- > EOF
$ hg init main
$ cat > main/.hg/hgrc <<EOF
- > [extensions]
- > eol =
- >
> [hooks]
> pretxnchangegroup = python:hgext.eol.hook
> EOF
@@ -47,10 +40,12 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
- error: pretxnchangegroup hook failed: a.txt should not have CRLF line endings
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ a.txt in a8ee6548cd86 should not have CRLF line endings
transaction abort!
rollback completed
- abort: a.txt should not have CRLF line endings
+ abort: end-of-line check failed:
+ a.txt in a8ee6548cd86 should not have CRLF line endings
[255]
$ printf "first\nsecond\nthird\n" > a.txt
@@ -73,10 +68,12 @@
adding manifests
adding file changes
added 1 changesets with 1 changes to 1 files
- error: pretxnchangegroup hook failed: crlf.txt should not have LF line endings
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ crlf.txt in 004ba2132725 should not have LF line endings
transaction abort!
rollback completed
- abort: crlf.txt should not have LF line endings
+ abort: end-of-line check failed:
+ crlf.txt in 004ba2132725 should not have LF line endings
[255]
$ printf "first\r\nsecond\r\nthird\r\n" > crlf.txt
@@ -88,3 +85,133 @@
adding manifests
adding file changes
added 2 changesets with 2 changes to 1 files
+
+ $ printf "first\r\nsecond" > b.txt
+ $ hg add b.txt
+ $ hg commit -m 'CRLF b.txt'
+ $ hg push ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
+
+ $ hg up -r -2
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ printf "some\nother\nfile" > c.txt
+ $ hg add c.txt
+ $ hg commit -m "LF c.txt, b.txt doesn't exist here"
+ created new head
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
+
+Test checkheadshook alias
+
+ $ cat > ../main/.hg/hgrc <<EOF
+ > [hooks]
+ > pretxnchangegroup = python:hgext.eol.checkheadshook
+ > EOF
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 2 files (+1 heads)
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
+
+We can fix the head and push again
+
+ $ hg up 6
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ printf "first\nsecond" > b.txt
+ $ hg ci -m "remove CRLF from b.txt"
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 2 files (+1 heads)
+ $ hg -R ../main rollback
+ repository tip rolled back to revision 5 (undo push)
+ working directory now based on revision -1
+
+Test it still fails with checkallhook
+
+ $ cat > ../main/.hg/hgrc <<EOF
+ > [hooks]
+ > pretxnchangegroup = python:hgext.eol.checkallhook
+ > EOF
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 2 files (+1 heads)
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
+
+But we can push the clean head
+
+ $ hg push -r7 -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 1 changesets with 1 changes to 1 files
+
+Test multiple files/revisions output
+
+ $ printf "another\r\nbad\r\none" > d.txt
+ $ hg add d.txt
+ $ hg ci -m "add d.txt"
+ $ hg push -f ../main
+ pushing to ../main
+ searching for changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 3 changesets with 3 changes to 2 files (+1 heads)
+ error: pretxnchangegroup hook failed: end-of-line check failed:
+ d.txt in a7040e68714f should not have CRLF line endings
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ transaction abort!
+ rollback completed
+ abort: end-of-line check failed:
+ d.txt in a7040e68714f should not have CRLF line endings
+ b.txt in fbcf9b1025f5 should not have CRLF line endings
+ [255]
--- a/tests/test-eol-patch.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-eol-patch.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,6 +1,6 @@
Test EOL patching
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [diff]
> git = 1
> EOF
--- a/tests/test-eol-tag.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-eol-tag.t Wed Mar 23 12:38:36 2011 -0500
@@ -2,10 +2,7 @@
Testing tagging with the EOL extension
- $ cat > $HGRCPATH <<EOF
- > [diff]
- > git = True
- >
+ $ cat >> $HGRCPATH <<EOF
> [extensions]
> eol =
>
--- a/tests/test-eol-update.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-eol-update.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,6 +1,6 @@
Test EOL update
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [diff]
> git = 1
> EOF
--- a/tests/test-eol.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-eol.t Wed Mar 23 12:38:36 2011 -0500
@@ -1,6 +1,6 @@
Test EOL extension
- $ cat > $HGRCPATH <<EOF
+ $ cat >> $HGRCPATH <<EOF
> [diff]
> git = True
> EOF
--- a/tests/test-extdiff.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-extdiff.t Wed Mar 23 12:38:36 2011 -0500
@@ -168,3 +168,12 @@
diffing this a.8a5febb7f867/a a.34eed99112ab/a
[1]
+Test with revsets:
+
+ $ hg extdif -p echo -c "rev(1)"
+ a.8a5febb7f867/a a.34eed99112ab/a
+ [1]
+
+ $ hg extdif -p echo -r "0::1"
+ a.8a5febb7f867/a a.34eed99112ab/a
+ [1]
--- a/tests/test-help.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-help.t Wed Mar 23 12:38:36 2011 -0500
@@ -765,6 +765,14 @@
working directory is checked out, it is equivalent to null. If an
uncommitted merge is in progress, "." is the revision of the first parent.
+Test templating help
+
+ $ hg help templating | egrep '(desc|diffstat|firstline|nonempty) '
+ desc String. The text of the changeset description.
+ diffstat String. Statistics of changes with the following format:
+ firstline Any text. Returns the first line of text.
+ nonempty Any text. Returns '(none)' if the string is empty.
+
Test help hooks
$ cat > helphook1.py <<EOF
--- a/tests/test-hgrc.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-hgrc.t Wed Mar 23 12:38:36 2011 -0500
@@ -20,12 +20,12 @@
$ cd foobar
$ cat .hg/hgrc
[paths]
- default = */foo%bar (glob)
+ default = $TESTTMP/foo%bar
$ hg paths
- default = */foo%bar (glob)
+ default = $TESTTMP/foo%bar
$ hg showconfig
- bundle.mainreporoot=*/foobar (glob)
- paths.default=*/foo%bar (glob)
+ bundle.mainreporoot=$TESTTMP/foobar
+ paths.default=$TESTTMP/foo%bar
$ cd ..
issue1829: wrong indentation
--- a/tests/test-hgweb-commands.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-hgweb-commands.t Wed Mar 23 12:38:36 2011 -0500
@@ -905,7 +905,7 @@
$ "$TESTDIR/get-with-headers.py" 127.0.0.1:$HGPORT '?cmd=capabilities'; echo
200 Script output follows
- lookup changegroupsubset branchmap pushkey unbundle=HG10GZ,HG10BZ,HG10UN
+ lookup changegroupsubset branchmap pushkey known unbundle=HG10GZ,HG10BZ,HG10UN
heads
--- a/tests/test-http-proxy.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-http-proxy.t Wed Mar 23 12:38:36 2011 -0500
@@ -98,27 +98,23 @@
updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cat proxy.log
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=stream_out HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
+ * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?pairs=0000000000000000000000000000000000000000-0000000000000000000000000000000000000000&cmd=between HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=heads HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=changegroup&roots=0000000000000000000000000000000000000000 HTTP/1.1" - - (glob)
- * - - [*] "GET http://localhost:$HGPORT/?cmd=capabilities HTTP/1.1" - - (glob)
* - - [*] "GET http://localhost:$HGPORT/?cmd=listkeys&namespace=bookmarks HTTP/1.1" - - (glob)
--- a/tests/test-https.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-https.t Wed Mar 23 12:38:36 2011 -0500
@@ -118,9 +118,9 @@
adding manifests
adding file changes
added 1 changesets with 4 changes to 4 files
- warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
updating to branch default
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ warning: localhost certificate with fingerprint 91:4f:1a:ff:87:24:9c:09:b6:85:9b:88:b1:90:6d:30:75:64:91:ca not verified (check hostfingerprints or web.cacerts config setting)
$ hg verify -R copy-pull
checking changesets
checking manifests
--- a/tests/test-identify.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-identify.t Wed Mar 23 12:38:36 2011 -0500
@@ -65,26 +65,43 @@
remote with rev number?
$ hg id -n http://localhost:$HGPORT1/
- abort: can't query remote revision number, branch, tags, or bookmarks
+ abort: can't query remote revision number, branch, or tags
[255]
remote with tags?
$ hg id -t http://localhost:$HGPORT1/
- abort: can't query remote revision number, branch, tags, or bookmarks
+ abort: can't query remote revision number, branch, or tags
[255]
remote with branch?
$ hg id -b http://localhost:$HGPORT1/
- abort: can't query remote revision number, branch, tags, or bookmarks
+ abort: can't query remote revision number, branch, or tags
[255]
-remote with bookmarks?
+test bookmark support
- $ hg id -B http://localhost:$HGPORT1/
- abort: can't query remote revision number, branch, tags, or bookmarks
- [255]
+ $ hg bookmark Y
+ $ hg bookmark Z
+ $ hg bookmarks
+ Y 0:cb9a9f314b8b
+ * Z 0:cb9a9f314b8b
+ $ hg id
+ cb9a9f314b8b+ tip Y/Z
+ $ hg id --bookmarks
+ Y Z
+
+test remote identify with bookmarks
+
+ $ hg id http://localhost:$HGPORT1/
+ cb9a9f314b8b Y/Z
+ $ hg id --bookmarks http://localhost:$HGPORT1/
+ Y Z
+ $ hg id -r . http://localhost:$HGPORT1/
+ cb9a9f314b8b Y/Z
+ $ hg id --bookmarks -r . http://localhost:$HGPORT1/
+ Y Z
Make sure we do not obscure unknown requires file entries (issue2649)
--- a/tests/test-init.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-init.t Wed Mar 23 12:38:36 2011 -0500
@@ -199,3 +199,17 @@
store
fncache
dotencode
+
+clone bookmarks
+
+ $ hg -R local bookmark test
+ $ hg -R local bookmarks
+ * test 0:08b9e9f63b32
+ $ hg clone -e "python ./dummyssh" local ssh://user@dummy/remote-bookmarks
+ searching for changes
+ remote: adding changesets
+ remote: adding manifests
+ remote: adding file changes
+ remote: added 1 changesets with 1 changes to 1 files
+ $ hg -R remote-bookmarks bookmarks
+ test 0:08b9e9f63b32
--- a/tests/test-keyword.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-keyword.t Wed Mar 23 12:38:36 2011 -0500
@@ -209,7 +209,7 @@
To: Test
changeset a2392c293916 in $TESTTMP/Test
- details: *cmd=changeset;node=a2392c293916 (glob)
+ details: $TESTTMP/Test?cmd=changeset;node=a2392c293916
description:
addsym
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-known.t Wed Mar 23 12:38:36 2011 -0500
@@ -0,0 +1,37 @@
+
+= Test the known() protocol function =
+
+Create a test repository:
+
+ $ hg init repo
+ $ cd repo
+ $ touch a ; hg add a ; hg ci -ma
+ $ touch b ; hg add b ; hg ci -mb
+ $ touch c ; hg add c ; hg ci -mc
+ $ hg log --template '{node}\n'
+ 991a3460af53952d10ec8a295d3d2cc2e5fa9690
+ 0e067c57feba1a5694ca4844f05588bb1bf82342
+ 3903775176ed42b1458a6281db4a0ccf4d9f287a
+ $ cd ..
+
+Test locally:
+
+ $ hg debugknown repo 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a
+ 111
+ $ hg debugknown repo 000a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0003775176ed42b1458a6281db4a0ccf4d9f287a
+ 010
+ $ hg debugknown repo
+
+
+Test via HTTP:
+
+ $ hg serve -R repo -p $HGPORT -d --pid-file=hg.pid -E error.log -A access.log
+ $ cat hg.pid >> $DAEMON_PIDS
+ $ hg debugknown http://localhost:$HGPORT/ 991a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 3903775176ed42b1458a6281db4a0ccf4d9f287a
+ 111
+ $ hg debugknown http://localhost:$HGPORT/ 000a3460af53952d10ec8a295d3d2cc2e5fa9690 0e067c57feba1a5694ca4844f05588bb1bf82342 0003775176ed42b1458a6281db4a0ccf4d9f287a
+ 010
+ $ hg debugknown http://localhost:$HGPORT/
+
+ $ cat error.log
+
--- a/tests/test-mq-strip.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-mq-strip.t Wed Mar 23 12:38:36 2011 -0500
@@ -410,7 +410,7 @@
abort: local changes found
[255]
$ hg strip tip --keep
- saved backup bundle to * (glob)
+ saved backup bundle to $TESTTMP/test/.hg/strip-backup/*-backup.hg (glob)
$ hg log --graph
@ changeset: 0:9ab35a2d17cb
tag: tip
--- a/tests/test-rebase-collapse.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-rebase-collapse.t Wed Mar 23 12:38:36 2011 -0500
@@ -137,6 +137,40 @@
$ cd ..
+Rebasing G onto H with custom message:
+
+ $ hg clone -q -u . a a3
+ $ cd a3
+
+ $ hg rebase --base 6 -m 'custom message'
+ abort: message can only be specified with collapse
+ [255]
+
+ $ hg rebase --base 6 --collapse -m 'custom message'
+ saved backup bundle to $TESTTMP/a3/.hg/strip-backup/*-backup.hg (glob)
+
+ $ hg tglog
+ @ 6: 'custom message'
+ |
+ o 5: 'H'
+ |
+ o 4: 'F'
+ |
+ | o 3: 'D'
+ | |
+ | o 2: 'C'
+ | |
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ hg manifest
+ A
+ E
+ F
+ H
+
+ $ cd ..
Create repo b:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-rebase-named-branches.t Wed Mar 23 12:38:36 2011 -0500
@@ -0,0 +1,171 @@
+ $ cat >> $HGRCPATH <<EOF
+ > [extensions]
+ > graphlog=
+ > rebase=
+ >
+ > [alias]
+ > tglog = log -G --template "{rev}: '{desc}' {branches}\n"
+ > EOF
+
+
+ $ hg init a
+ $ cd a
+
+ $ echo A > A
+ $ hg ci -Am A
+ adding A
+
+ $ echo B > B
+ $ hg ci -Am B
+ adding B
+
+ $ hg up -q -C 0
+
+ $ echo C > C
+ $ hg ci -Am C
+ adding C
+ created new head
+
+ $ hg up -q -C 0
+
+ $ echo D > D
+ $ hg ci -Am D
+ adding D
+ created new head
+
+ $ hg merge -r 2
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+
+ $ hg ci -m E
+
+ $ hg up -q -C 3
+
+ $ echo F > F
+ $ hg ci -Am F
+ adding F
+ created new head
+
+ $ cd ..
+
+
+Rebasing descendant onto ancestor across different named branches
+
+ $ hg clone -q -u . a a1
+
+ $ cd a1
+
+ $ hg branch dev
+ marked working directory as branch dev
+
+ $ echo x > x
+
+ $ hg add x
+
+ $ hg ci -m 'extra named branch'
+
+ $ hg tglog
+ @ 6: 'extra named branch' dev
+ |
+ o 5: 'F'
+ |
+ | o 4: 'E'
+ |/|
+ o | 3: 'D'
+ | |
+ | o 2: 'C'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ hg rebase -s 6 -d 5
+ saved backup bundle to $TESTTMP/a1/.hg/strip-backup/*-backup.hg (glob)
+
+ $ hg tglog
+ @ 6: 'extra named branch'
+ |
+ o 5: 'F'
+ |
+ | o 4: 'E'
+ |/|
+ o | 3: 'D'
+ | |
+ | o 2: 'C'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ cd ..
+
+Rebasing descendant onto ancestor across the same named branches
+
+ $ hg clone -q -u . a a2
+
+ $ cd a2
+
+ $ echo x > x
+
+ $ hg add x
+
+ $ hg ci -m 'G'
+
+ $ hg tglog
+ @ 6: 'G'
+ |
+ o 5: 'F'
+ |
+ | o 4: 'E'
+ |/|
+ o | 3: 'D'
+ | |
+ | o 2: 'C'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ hg rebase -s 6 -d 5
+ abort: source is descendant of destination
+ [255]
+
+ $ cd ..
+
+Rebasing ancestor onto descendant across different named branches
+
+ $ hg clone -q -u . a a3
+
+ $ cd a3
+
+ $ hg branch dev
+ marked working directory as branch dev
+
+ $ echo x > x
+
+ $ hg add x
+
+ $ hg ci -m 'extra named branch'
+
+ $ hg tglog
+ @ 6: 'extra named branch' dev
+ |
+ o 5: 'F'
+ |
+ | o 4: 'E'
+ |/|
+ o | 3: 'D'
+ | |
+ | o 2: 'C'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+ $ hg rebase -s 5 -d 6
+ abort: source is ancestor of destination
+ [255]
+
+ $ cd ..
+
+
--- a/tests/test-relink.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-relink.t Wed Mar 23 12:38:36 2011 -0500
@@ -20,23 +20,29 @@
$ hg init repo
$ cd repo
- $ echo '[ui]' > .hg/hgrc
- $ echo 'username= A. Foo <a.foo@bar.com>' >> .hg/hgrc
$ echo a > a
$ echo b > b
$ hg ci -Am addfile
adding a
adding b
- $ echo a >> a
- $ echo a >> b
+ $ cat $TESTDIR/binfile.bin >> a
+ $ cat $TESTDIR/binfile.bin >> b
$ hg ci -Am changefiles
+make another commit to create files larger than 1 KB to test
+formatting of final byte count
+
+ $ cat $TESTDIR/binfile.bin >> a
+ $ cat $TESTDIR/binfile.bin >> b
+ $ hg ci -m anotherchange
+
don't sit forever trying to double-lock the source repo
$ hg relink .
relinking $TESTTMP/repo/.hg/store to $TESTTMP/repo/.hg/store
there is nothing to relink
+
Test files are read in binary mode
$ python -c "file('.hg/store/data/dummy.i', 'wb').write('a\r\nb\n')"
@@ -53,8 +59,6 @@
updating to branch default
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd clone
- $ echo '[ui]' >> .hg/hgrc
- $ echo 'username= A. Baz <a.baz@bar.com>' >> .hg/hgrc
$ hg pull -q
$ echo b >> b
$ hg ci -m changeb
@@ -81,7 +85,7 @@
pruned down to 2 probably relinkable files
relinking: data/a.i 1/2 files (50.00%)
not linkable: data/dummy.i
- relinked 1 files (136 bytes reclaimed)
+ relinked 1 files (1.37 KB reclaimed)
$ cd ..
--- a/tests/test-rename-merge1.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-rename-merge1.t Wed Mar 23 12:38:36 2011 -0500
@@ -131,27 +131,27 @@
$ hg init repo2089
$ cd repo2089
- $ echo 0 > A
- $ hg -q ci -Am 0
+ $ echo c0 > f1
+ $ hg ci -Aqm0
- $ hg -q up -C null
- $ echo 1 > A
- $ hg -q ci -Am 1
+ $ hg up null -q
+ $ echo c1 > f1
+ $ hg ci -Aqm1
- $ hg -q up -C 0
+ $ hg up 0 -q
$ hg merge 1 -q --tool internal:local
- $ echo 2 > A
- $ hg -q ci -m 2
+ $ echo c2 > f1
+ $ hg ci -qm2
- $ hg -q up -C 1
- $ hg mv A a
- $ hg -q ci -Am 3
+ $ hg up 1 -q
+ $ hg mv f1 f2
+ $ hg ci -Aqm3
- $ hg -q up -C 2
+ $ hg up 2 -q
$ hg merge 3
- merging A and a to a
+ merging f1 and f2 to f2
0 files updated, 1 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
- $ cat a
- 2
+ $ cat f2
+ c2
--- a/tests/test-revset-outgoing.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-revset-outgoing.t Wed Mar 23 12:38:36 2011 -0500
@@ -39,7 +39,7 @@
$ cd b
$ cat .hg/hgrc
[paths]
- default = */a#stable (glob)
+ default = $TESTTMP/a#stable
$ echo red >> a
$ hg ci -qm3
@@ -60,7 +60,7 @@
$ hg tout
- comparing with */a (glob)
+ comparing with $TESTTMP/a
searching for changes
2:1d4099801a4e: '3' stable
@@ -79,11 +79,11 @@
$ cat .hg/hgrc
[paths]
- default = */a#stable (glob)
+ default = $TESTTMP/a#stable
green = ../a#default
$ hg tout green
- comparing with */a (glob)
+ comparing with $TESTTMP/a
searching for changes
3:f0461977a3db: '4'
--- a/tests/test-revset.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-revset.t Wed Mar 23 12:38:36 2011 -0500
@@ -356,3 +356,10 @@
9
$ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(é)))'
4
+
+issue2654: report a parse error if the revset was not completely parsed
+
+ $ log '1 OR 2'
+ hg: parse error at 2: invalid token
+ [255]
+
--- a/tests/test-schemes.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-schemes.t Wed Mar 23 12:38:36 2011 -0500
@@ -25,7 +25,7 @@
$ hg incoming --debug parts://localhost
using http://localhost:$HGPORT/
- sending between command
+ sending capabilities command
comparing with parts://localhost
sending heads command
searching for changes
--- a/tests/test-serve Wed Mar 23 13:58:33 2011 -0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-#!/bin/sh
-
-hgserve()
-{
- hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
- | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
- -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
- -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
- cat hg.pid >> "$DAEMON_PIDS"
- echo % errors
- cat errors.log
- sleep 1
- if [ "$KILLQUIETLY" = "Y" ]; then
- kill `cat hg.pid` 2>/dev/null
- else
- kill `cat hg.pid`
- fi
- sleep 1
-}
-
-hg init test
-cd test
-
-echo '[web]' > .hg/hgrc
-echo 'accesslog = access.log' >> .hg/hgrc
-echo "port = $HGPORT1" >> .hg/hgrc
-
-echo % Without -v
-hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
-cat hg.pid >> "$DAEMON_PIDS"
-if [ -f access.log ]; then
- echo 'access log created - .hg/hgrc respected'
-fi
-echo % errors
-cat errors.log
-
-echo % With -v
-hgserve
-
-echo % With -v and -p HGPORT2
-hgserve -p "$HGPORT2"
-
-echo '% With -v and -p daytime (should fail because low port)'
-KILLQUIETLY=Y
-hgserve -p daytime
-KILLQUIETLY=N
-
-echo % With --prefix foo
-hgserve --prefix foo
-
-echo % With --prefix /foo
-hgserve --prefix /foo
-
-echo % With --prefix foo/
-hgserve --prefix foo/
-
-echo % With --prefix /foo/
-hgserve --prefix /foo/
--- a/tests/test-serve.out Wed Mar 23 13:58:33 2011 -0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-% Without -v
-access log created - .hg/hgrc respected
-% errors
-% With -v
-listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
-% errors
-% With -v and -p HGPORT2
-listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
-% errors
-% With -v and -p daytime (should fail because low port)
-abort: cannot start server at 'localhost:13': Permission denied
-abort: child process failed to start
-% errors
-% With --prefix foo
-listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
-% errors
-% With --prefix /foo
-listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
-% errors
-% With --prefix foo/
-listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
-% errors
-% With --prefix /foo/
-listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
-% errors
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-serve.t Wed Mar 23 12:38:36 2011 -0500
@@ -0,0 +1,82 @@
+
+ $ hgserve()
+ > {
+ > hg serve -a localhost -d --pid-file=hg.pid -E errors.log -v $@ \
+ > | sed -e "s/:$HGPORT1\\([^0-9]\\)/:HGPORT1\1/g" \
+ > -e "s/:$HGPORT2\\([^0-9]\\)/:HGPORT2\1/g" \
+ > -e 's/http:\/\/[^/]*\//http:\/\/localhost\//'
+ > cat hg.pid >> "$DAEMON_PIDS"
+ > echo % errors
+ > cat errors.log
+ > sleep 1
+ > if [ "$KILLQUIETLY" = "Y" ]; then
+ > kill `cat hg.pid` 2>/dev/null
+ > else
+ > kill `cat hg.pid`
+ > fi
+ > sleep 1
+ > }
+
+ $ hg init test
+ $ cd test
+ $ echo '[web]' > .hg/hgrc
+ $ echo 'accesslog = access.log' >> .hg/hgrc
+ $ echo "port = $HGPORT1" >> .hg/hgrc
+
+Without -v
+
+ $ hg serve -a localhost -p $HGPORT -d --pid-file=hg.pid -E errors.log
+ $ cat hg.pid >> "$DAEMON_PIDS"
+ $ if [ -f access.log ]; then
+ $ echo 'access log created - .hg/hgrc respected'
+ access log created - .hg/hgrc respected
+ $ fi
+
+errors
+
+ $ cat errors.log
+
+With -v
+
+ $ hgserve
+ listening at http://localhost/ (bound to 127.0.0.1:HGPORT1)
+ % errors
+
+With -v and -p HGPORT2
+
+ $ hgserve -p "$HGPORT2"
+ listening at http://localhost/ (bound to 127.0.0.1:HGPORT2)
+ % errors
+
+With -v and -p daytime (should fail because low port)
+
+ $ KILLQUIETLY=Y
+ $ hgserve -p daytime
+ abort: cannot start server at 'localhost:13': Permission denied
+ abort: child process failed to start
+ % errors
+ $ KILLQUIETLY=N
+
+With --prefix foo
+
+ $ hgserve --prefix foo
+ listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
+ % errors
+
+With --prefix /foo
+
+ $ hgserve --prefix /foo
+ listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
+ % errors
+
+With --prefix foo/
+
+ $ hgserve --prefix foo/
+ listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
+ % errors
+
+With --prefix /foo/
+
+ $ hgserve --prefix /foo/
+ listening at http://localhost/foo/ (bound to 127.0.0.1:HGPORT1)
+ % errors
--- a/tests/test-ssh.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-ssh.t Wed Mar 23 12:38:36 2011 -0500
@@ -263,6 +263,22 @@
summary: z
+clone bookmarks
+
+ $ hg -R ../remote bookmark test
+ $ hg -R ../remote bookmarks
+ * test 2:6c0482d977a3
+ $ hg clone -e "python ../dummyssh" ssh://user@dummy/remote local-bookmarks
+ requesting all changes
+ adding changesets
+ adding manifests
+ adding file changes
+ added 4 changesets with 5 changes to 4 files (+1 heads)
+ updating to branch default
+ 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg -R local-bookmarks bookmarks
+ test 2:6c0482d977a3
+
passwords in ssh urls are not supported
$ hg push ssh://user:erroneouspwd@dummy/remote
@@ -290,3 +306,4 @@
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
Got arguments 1:user@dummy 2:hg -R remote serve --stdio
+ Got arguments 1:user@dummy 2:hg -R remote serve --stdio
--- a/tests/test-subrepo-git.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-subrepo-git.t Wed Mar 23 12:38:36 2011 -0500
@@ -73,7 +73,7 @@
$ cd t
$ hg clone . ../tc
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ../tc
$ hg debugsub
@@ -96,7 +96,7 @@
$ cd ../t
$ hg clone . ../ta
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ../ta
@@ -115,7 +115,7 @@
$ cd ../t
$ hg clone . ../tb
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ../tb/s
@@ -155,7 +155,7 @@
added 1 changesets with 1 changes to 1 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg merge 2>/dev/null
- pulling subrepo s
+ pulling subrepo s from $TESTTMP/gitroot
0 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ cat s/f
@@ -199,7 +199,7 @@
$ cd ../t
$ hg clone . ../td
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
checking out detached HEAD in subrepo s
check out a git branch if you intend to make changes
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -232,7 +232,7 @@
$ cd ../tb
$ hg pull -q
$ hg update 2>/dev/null
- pulling subrepo s
+ pulling subrepo s from $TESTTMP/gitroot
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg debugsub
path s
@@ -262,7 +262,7 @@
$ cd ../tc
$ hg pull -q
$ hg archive --subrepos -r 5 ../archive 2>/dev/null
- pulling subrepo s
+ pulling subrepo s from $TESTTMP/gitroot
$ cd ../archive
$ cat s/f
f
@@ -282,7 +282,7 @@
$ hg clone ../t inner
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ echo inner = inner > .hgsub
$ hg add .hgsub
@@ -311,7 +311,7 @@
$ mkdir d
$ hg clone t d/t
updating to branch default
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
Don't crash if the subrepo is missing
@@ -329,7 +329,7 @@
abort: subrepo s is missing
[255]
$ hg update -C
- cloning subrepo s
+ cloning subrepo s from $TESTTMP/gitroot
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg sum | grep commit
commit: (clean)
--- a/tests/test-subrepo-paths.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-subrepo-paths.t Wed Mar 23 12:38:36 2011 -0500
@@ -21,6 +21,15 @@
source C:\libs\foo-lib\
revision
+test cumulative remapping, the $HGRCPATH file is loaded first
+
+ $ echo '[subpaths]' >> $HGRCPATH
+ $ echo 'libfoo = libbar' >> $HGRCPATH
+ $ hg debugsub
+ path sub
+ source C:\libs\bar-lib\
+ revision
+
test bad subpaths pattern
$ cat > .hg/hgrc <<EOF
--- a/tests/test-transplant.t Wed Mar 23 13:58:33 2011 -0300
+++ b/tests/test-transplant.t Wed Mar 23 12:38:36 2011 -0500
@@ -68,6 +68,18 @@
$ hg help revsets | grep transplanted
"transplanted(set)"
+test tranplanted keyword
+
+ $ hg log --template '{rev} {transplanted}\n'
+ 7 a53251cdf717679d1907b289f991534be05c997a
+ 6 722f4667af767100cb15b6a79324bf8abbfe1ef4
+ 5 37a1297eb21b3ef5c5d2ffac22121a0988ed9f21
+ 4
+ 3
+ 2
+ 1
+ 0
+
$ hg clone ../t ../prune
updating to branch default
4 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -330,6 +342,27 @@
[255]
$ cd ..
+test environment passed to filter
+
+ $ hg init filter-environment
+ $ cd filter-environment
+ $ cat <<'EOF' >test-filter-environment
+ > #!/bin/sh
+ > echo "Transplant by $HGUSER" >> $1
+ > echo "Transplant from rev $HGREVISION" >> $1
+ > EOF
+ $ chmod +x test-filter-environment
+ $ hg transplant -s ../t --filter ./test-filter-environment 0
+ filtering * (glob)
+ applying 17ab29e464c6
+ 17ab29e464c6 transplanted to 5190e68026a0
+
+ $ hg log --template '{rev} {parents} {desc}\n'
+ 0 r1
+ Transplant by test
+ Transplant from rev 17ab29e464c6ca53e329470efe2a9918ac617a6f
+ $ cd ..
+
test with a win32ext like setup (differing EOLs)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-wireproto.t Wed Mar 23 12:38:36 2011 -0500
@@ -0,0 +1,60 @@
+
+Test wire protocol argument passing
+
+Setup repo:
+
+ $ hg init repo
+
+Local:
+
+ $ hg debugwireargs repo eins zwei --three drei --four vier
+ eins zwei drei vier
+ $ hg debugwireargs repo eins zwei --four vier
+ eins zwei None vier
+ $ hg debugwireargs repo eins zwei
+ eins zwei None None
+
+HTTP:
+
+ $ hg serve -R repo -p $HGPORT -d --pid-file=hg1.pid -E error.log -A access.log
+ $ cat hg1.pid >> $DAEMON_PIDS
+
+ $ hg debugwireargs http://localhost:$HGPORT/ un deux trois quatre
+ un deux trois quatre
+ $ hg debugwireargs http://localhost:$HGPORT/ eins zwei --four vier
+ eins zwei None vier
+ $ hg debugwireargs http://localhost:$HGPORT/ eins zwei
+ eins zwei None None
+ $ cat access.log
+ * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&four=quatre&one=un&three=trois&two=deux HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&four=quatre&one=un&three=trois&two=deux HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&four=vier&one=eins&two=zwei HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&four=vier&one=eins&two=zwei HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=capabilities HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
+ * - - [*] "GET /?cmd=debugwireargs&one=eins&two=zwei HTTP/1.1" 200 - (glob)
+
+SSH (try to exercise the ssh functionality with a dummy script):
+
+ $ cat <<EOF > dummyssh
+ > import sys
+ > import os
+ > os.chdir(os.path.dirname(sys.argv[0]))
+ > if sys.argv[1] != "user@dummy":
+ > sys.exit(-1)
+ > if not os.path.exists("dummyssh"):
+ > sys.exit(-1)
+ > os.environ["SSH_CLIENT"] = "127.0.0.1 1 2"
+ > r = os.system(sys.argv[2])
+ > sys.exit(bool(r))
+ > EOF
+
+ $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo uno due tre quattro
+ uno due tre quattro
+ $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei --four vier
+ eins zwei None vier
+ $ hg debugwireargs --ssh "python ./dummyssh" ssh://user@dummy/repo eins zwei
+ eins zwei None None
+