--- a/contrib/check-code.py Tue Sep 20 15:21:27 2011 +0300
+++ b/contrib/check-code.py Wed Sep 28 16:11:53 2011 -0500
@@ -148,7 +148,7 @@
(r'(?<!def)\s+(any|all|format)\(',
"any/all/format not available in Python 2.4"),
(r'(?<!def)\s+(callable)\(',
- "callable not available in Python 3, use hasattr(f, '__call__')"),
+ "callable not available in Python 3, use getattr(f, '__call__', None)"),
(r'if\s.*\selse', "if ... else form not available in Python 2.4"),
(r'^\s*(%s)\s\s' % '|'.join(keyword.kwlist),
"gratuitous whitespace after Python keyword"),
@@ -168,6 +168,8 @@
"comparison with singleton, use 'is' or 'is not' instead"),
(r'^\s*(while|if) [01]:',
"use True/False for constant Boolean expression"),
+ (r'(?<!def)\s+hasattr',
+ 'hasattr(foo, bar) is broken, use util.safehasattr(foo, bar) instead'),
(r'opener\([^)]*\).read\(',
"use opener.read() instead"),
(r'opener\([^)]*\).write\(',
--- a/contrib/setup3k.py Tue Sep 20 15:21:27 2011 +0300
+++ b/contrib/setup3k.py Wed Sep 28 16:11:53 2011 -0500
@@ -8,7 +8,7 @@
from lib2to3.refactor import get_fixers_from_package as getfixers
import sys
-if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
+if getattr(sys, 'version_info', (0, 0, 0)) < (2, 4, 0, 'final'):
raise SystemExit("Mercurial requires Python 2.4 or later.")
if sys.version_info[0] >= 3:
@@ -236,7 +236,7 @@
try:
build_ext.build_extension(self, ext)
except CCompilerError:
- if not hasattr(ext, 'optional') or not ext.optional:
+ if getattr(ext, 'optional', False):
raise
log.warn("Failed to build optional extension '%s' (skipping)",
ext.name)
--- a/contrib/win32/hgwebdir_wsgi.py Tue Sep 20 15:21:27 2011 +0300
+++ b/contrib/win32/hgwebdir_wsgi.py Wed Sep 28 16:11:53 2011 -0500
@@ -50,7 +50,7 @@
#sys.path.insert(0, r'c:\path\to\python\lib')
# Enable tracing. Run 'python -m win32traceutil' to debug
-if hasattr(sys, 'isapidllhandle'):
+if getattr(sys, 'isapidllhandle', None) is not None:
import win32traceutil
# To serve pages in local charset instead of UTF-8, remove the two lines below
--- a/contrib/zsh_completion Tue Sep 20 15:21:27 2011 +0300
+++ b/contrib/zsh_completion Wed Sep 28 16:11:53 2011 -0500
@@ -165,6 +165,7 @@
_hg_labels() {
_hg_tags "$@"
_hg_bookmarks "$@"
+ _hg_branches "$@"
}
_hg_tags() {
@@ -191,6 +192,17 @@
(( $#bookmarks )) && _describe -t bookmarks 'bookmarks' bookmarks
}
+_hg_branches() {
+ typeset -a branches
+ local branch
+
+ _hg_cmd branches | while read branch
+ do
+ branches+=(${branch/ # [0-9]#:*})
+ done
+ (( $#branches )) && _describe -t branches 'branches' branches
+}
+
# likely merge candidates
_hg_mergerevs() {
typeset -a heads
@@ -617,6 +629,7 @@
'(--only-merges -m)'{-m,--only-merges}'[show only merges]' \
'(--patch -p)'{-p,--patch}'[show patch]' \
'(--prune -P)'{-P+,--prune}'[do not display revision or any of its ancestors]:revision:_hg_labels' \
+ '(--branch -b)'{-b+,--branch}'[show changesets within the given named branch]:branch:_hg_branches' \
'*:files:_hg_files'
}
--- a/doc/gendoc.py Tue Sep 20 15:21:27 2011 +0300
+++ b/doc/gendoc.py Wed Sep 28 16:11:53 2011 -0500
@@ -9,6 +9,7 @@
from mercurial.i18n import _
from mercurial.help import helptable
from mercurial import extensions
+from mercurial import util
def get_desc(docstr):
if not docstr:
@@ -95,7 +96,7 @@
ui.write(".. _%s:\n" % name)
ui.write("\n")
section(ui, sec)
- if hasattr(doc, '__call__'):
+ if util.safehasattr(doc, '__call__'):
doc = doc()
ui.write(doc)
ui.write("\n")
--- a/hgext/color.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/color.py Wed Sep 28 16:11:53 2011 -0500
@@ -68,6 +68,9 @@
branches.current = green
branches.inactive = none
+ tags.normal = green
+ tags.local = black bold
+
The available effects in terminfo mode are 'blink', 'bold', 'dim',
'inverse', 'invisible', 'italic', 'standout', and 'underline'; in
ECMA-48 mode, the options are 'bold', 'inverse', 'italic', and
@@ -257,7 +260,9 @@
'status.ignored': 'black bold',
'status.modified': 'blue bold',
'status.removed': 'red bold',
- 'status.unknown': 'magenta bold underline'}
+ 'status.unknown': 'magenta bold underline',
+ 'tags.normal': 'green',
+ 'tags.local': 'black bold'}
def _effect_str(effect):
--- a/hgext/convert/cvsps.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/convert/cvsps.py Wed Sep 28 16:11:53 2011 -0500
@@ -11,6 +11,7 @@
from mercurial import util
from mercurial.i18n import _
from mercurial import hook
+from mercurial import util
class logentry(object):
'''Class logentry has the following attributes:
@@ -513,8 +514,8 @@
e.comment == c.comment and
e.author == c.author and
e.branch == c.branch and
- (not hasattr(e, 'branchpoints') or
- not hasattr (c, 'branchpoints') or
+ (not util.safehasattr(e, 'branchpoints') or
+ not util.safehasattr (c, 'branchpoints') or
e.branchpoints == c.branchpoints) and
((c.date[0] + c.date[1]) <=
(e.date[0] + e.date[1]) <=
--- a/hgext/convert/filemap.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/convert/filemap.py Wed Sep 28 16:11:53 2011 -0500
@@ -375,3 +375,6 @@
def lookuprev(self, rev):
return self.base.lookuprev(rev)
+
+ def getbookmarks(self):
+ return self.base.getbookmarks()
--- a/hgext/convert/git.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/convert/git.py Wed Sep 28 16:11:53 2011 -0500
@@ -16,7 +16,7 @@
# Windows does not support GIT_DIR= construct while other systems
# cannot remove environment variable. Just assume none have
# both issues.
- if hasattr(os, 'unsetenv'):
+ if util.safehasattr(os, 'unsetenv'):
def gitopen(self, s, noerr=False):
prevgitdir = os.environ.get('GIT_DIR')
os.environ['GIT_DIR'] = self.path
--- a/hgext/convert/hg.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/convert/hg.py Wed Sep 28 16:11:53 2011 -0500
@@ -70,10 +70,10 @@
self.wlock.release()
def revmapfile(self):
- return os.path.join(self.path, ".hg", "shamap")
+ return self.repo.join("shamap")
def authorfile(self):
- return os.path.join(self.path, ".hg", "authormap")
+ return self.repo.join("authormap")
def getheads(self):
h = self.repo.changelog.heads()
@@ -364,8 +364,7 @@
def converted(self, rev, destrev):
if self.convertfp is None:
- self.convertfp = open(os.path.join(self.path, '.hg', 'shamap'),
- 'a')
+ self.convertfp = open(self.repo.join('shamap'), 'a')
self.convertfp.write('%s %s\n' % (destrev, rev))
self.convertfp.flush()
--- a/hgext/convert/subversion.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/convert/subversion.py Wed Sep 28 16:11:53 2011 -0500
@@ -501,11 +501,11 @@
and not p[2].startswith(badroot + '/')]
# Tell tag renamings from tag creations
- remainings = []
+ renamings = []
for source, sourcerev, dest in pendings:
tagname = dest.split('/')[-1]
if source.startswith(srctagspath):
- remainings.append([source, sourcerev, tagname])
+ renamings.append([source, sourcerev, tagname])
continue
if tagname in tags:
# Keep the latest tag value
@@ -521,7 +521,7 @@
# but were really created in the tag
# directory.
pass
- pendings = remainings
+ pendings = renamings
tagspath = srctagspath
finally:
stream.close()
--- a/hgext/convert/transport.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/convert/transport.py Wed Sep 28 16:11:53 2011 -0500
@@ -54,7 +54,7 @@
if p:
providers.append(p)
else:
- if hasattr(svn.client, 'get_windows_simple_provider'):
+ if util.safehasattr(svn.client, 'get_windows_simple_provider'):
providers.append(svn.client.get_windows_simple_provider(pool))
return svn.core.svn_auth_open(providers, pool)
@@ -73,7 +73,7 @@
self.password = ''
# Only Subversion 1.4 has reparent()
- if ra is None or not hasattr(svn.ra, 'reparent'):
+ if ra is None or not util.safehasattr(svn.ra, 'reparent'):
self.client = svn.client.create_context(self.pool)
ab = _create_auth_baton(self.pool)
if False:
--- a/hgext/eol.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/eol.py Wed Sep 28 16:11:53 2011 -0500
@@ -52,9 +52,10 @@
The rules will first apply when files are touched in the working
copy, e.g. by updating to null and back to tip to touch all files.
-The extension uses an optional ``[eol]`` section in your hgrc file
-(not the ``.hgeol`` file) for settings that control the overall
-behavior. There are two settings:
+The extension uses an optional ``[eol]`` section read from both the
+normal Mercurial configuration files and the ``.hgeol`` file, with the
+latter overriding the former. You can use that section to control the
+overall behavior. There are three settings:
- ``eol.native`` (default ``os.linesep``) can be set to ``LF`` or
``CRLF`` to override the default interpretation of ``native`` for
@@ -67,6 +68,10 @@
Such files are normally not touched under the assumption that they
have mixed EOLs on purpose.
+- ``eol.fix-trailing-newline`` (default False) can be set to True to
+ ensure that converted files end with a EOL character (either ``\\n``
+ or ``\\r\\n`` as per the configured patterns).
+
The extension provides ``cleverencode:`` and ``cleverdecode:`` filters
like the deprecated win32text extension does. This means that you can
disable win32text and enable eol and your filters will still work. You
@@ -106,6 +111,8 @@
return s
if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
return s
+ if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
+ s = s + '\n'
return eolre.sub('\n', s)
def tocrlf(s, params, ui, **kwargs):
@@ -114,6 +121,8 @@
return s
if ui.configbool('eol', 'only-consistent', True) and inconsistenteol(s):
return s
+ if ui.configbool('eol', 'fix-trailing-newline', False) and s and s[-1] != '\n':
+ s = s + '\n'
return eolre.sub('\r\n', s)
def isbinary(s, params):
@@ -158,7 +167,7 @@
# about inconsistent newlines.
self.match = match.match(root, '', [], include, exclude)
- def setfilters(self, ui):
+ def copytoui(self, ui):
for pattern, style in self.cfg.items('patterns'):
key = style.upper()
try:
@@ -167,6 +176,9 @@
except KeyError:
ui.warn(_("ignoring unknown EOL style '%s' from %s\n")
% (style, self.cfg.source('patterns', pattern)))
+ # eol.only-consistent can be specified in ~/.hgrc or .hgeol
+ for k, v in self.cfg.items('eol'):
+ ui.setconfig('eol', k, v)
def checkrev(self, repo, ctx, files):
failed = []
@@ -273,7 +285,7 @@
eol = parseeol(self.ui, self, nodes)
if eol is None:
return None
- eol.setfilters(self.ui)
+ eol.copytoui(self.ui)
return eol.match
def _hgcleardirstate(self):
--- a/hgext/inotify/__init__.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/inotify/__init__.py Wed Sep 28 16:11:53 2011 -0500
@@ -11,6 +11,7 @@
# todo: socket permissions
from mercurial.i18n import _
+from mercurial import util
import server
from client import client, QueryFailed
@@ -31,7 +32,7 @@
ui.write((' %s/\n') % path)
def reposetup(ui, repo):
- if not hasattr(repo, 'dirstate'):
+ if not util.safehasattr(repo, 'dirstate'):
return
class inotifydirstate(repo.dirstate.__class__):
--- a/hgext/keyword.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/keyword.py Wed Sep 28 16:11:53 2011 -0500
@@ -249,10 +249,14 @@
kwcmd = self.restrict and lookup # kwexpand/kwshrink
if self.restrict or expand and lookup:
mf = ctx.manifest()
- lctx = ctx
- re_kw = (self.restrict or rekw) and self.rekw or self.rekwexp
- msg = (expand and _('overwriting %s expanding keywords\n')
- or _('overwriting %s shrinking keywords\n'))
+ if self.restrict or rekw:
+ re_kw = self.rekw
+ else:
+ re_kw = self.rekwexp
+ if expand:
+ msg = _('overwriting %s expanding keywords\n')
+ else:
+ msg = _('overwriting %s shrinking keywords\n')
for f in candidates:
if self.restrict:
data = self.repo.file(f).read(mf[f])
@@ -262,18 +266,17 @@
continue
if expand:
if lookup:
- lctx = self.linkctx(f, mf[f])
- data, found = self.substitute(data, f, lctx, re_kw.subn)
+ ctx = self.linkctx(f, mf[f])
+ data, found = self.substitute(data, f, ctx, re_kw.subn)
elif self.restrict:
found = re_kw.search(data)
else:
data, found = _shrinktext(data, re_kw.subn)
if found:
self.ui.note(msg % f)
- fpath = self.repo.wjoin(f)
- mode = os.lstat(fpath).st_mode
- self.repo.wwrite(f, data, ctx.flags(f))
- os.chmod(fpath, mode)
+ fp = self.repo.wopener(f, "wb", atomictemp=True)
+ fp.write(data)
+ fp.close()
if kwcmd:
self.repo.dirstate.normal(f)
elif self.record:
@@ -296,7 +299,9 @@
def wread(self, fname, data):
'''If in restricted mode returns data read from wdir with
keyword substitutions removed.'''
- return self.restrict and self.shrink(fname, data) or data
+ if self.restrict:
+ return self.shrink(fname, data)
+ return data
class kwfilelog(filelog.filelog):
'''
@@ -325,11 +330,11 @@
text = self.kwt.shrink(self.path, text)
return super(kwfilelog, self).cmp(node, text)
-def _status(ui, repo, kwt, *pats, **opts):
+def _status(ui, repo, wctx, kwt, *pats, **opts):
'''Bails out if [keyword] configuration is not active.
Returns status of working directory.'''
if kwt:
- return repo.status(match=scmutil.match(repo[None], pats, opts), clean=True,
+ return repo.status(match=scmutil.match(wctx, pats, opts), clean=True,
unknown=opts.get('unknown') or opts.get('all'))
if ui.configitems('keyword'):
raise util.Abort(_('[keyword] patterns cannot match'))
@@ -343,7 +348,7 @@
kwt = kwtools['templater']
wlock = repo.wlock()
try:
- status = _status(ui, repo, kwt, *pats, **opts)
+ status = _status(ui, repo, wctx, kwt, *pats, **opts)
modified, added, removed, deleted, unknown, ignored, clean = status
if modified or added or removed or deleted:
raise util.Abort(_('outstanding uncommitted changes'))
@@ -415,7 +420,10 @@
ui.setconfig('keywordmaps', k, v)
else:
ui.status(_('\n\tconfiguration using current keyword template maps\n'))
- kwmaps = dict(uikwmaps) or _defaultkwmaps(ui)
+ if uikwmaps:
+ kwmaps = dict(uikwmaps)
+ else:
+ kwmaps = _defaultkwmaps(ui)
uisetup(ui)
reposetup(ui, repo)
@@ -478,13 +486,13 @@
i = ignored (not tracked)
'''
kwt = kwtools['templater']
- status = _status(ui, repo, kwt, *pats, **opts)
+ wctx = repo[None]
+ status = _status(ui, repo, wctx, kwt, *pats, **opts)
cwd = pats and repo.getcwd() or ''
modified, added, removed, deleted, unknown, ignored, clean = status
files = []
if not opts.get('unknown') or opts.get('all'):
files = sorted(modified + added + clean)
- wctx = repo[None]
kwfiles = kwt.iskwfile(files, wctx)
kwdeleted = kwt.iskwfile(deleted, wctx)
kwunknown = kwt.iskwfile(unknown, wctx)
--- a/hgext/mq.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/mq.py Wed Sep 28 16:11:53 2011 -0500
@@ -295,7 +295,8 @@
n, name = entry
yield statusentry(bin(n), name)
elif l.strip():
- self.ui.warn(_('malformated mq status line: %s\n') % entry)
+ msg = _('malformated mq status line: %s\n') % entry
+ self.ui.warn(msg)
# else we ignore empty lines
lines = self.opener.read(self.statuspath).splitlines()
return list(parselines(lines))
@@ -626,6 +627,7 @@
self.ui.note(str(inst) + '\n')
if not self.ui.verbose:
self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
+ self.ui.traceback()
return (False, list(files), False)
def apply(self, repo, series, list=False, update_status=True,
@@ -938,7 +940,7 @@
p.write("# User " + user + "\n")
if date:
p.write("# Date %s %s\n\n" % date)
- if hasattr(msg, '__call__'):
+ if util.safehasattr(msg, '__call__'):
msg = msg()
commitmsg = msg and msg or ("[mq]: %s" % patchfn)
n = repo.commit(commitmsg, user, date, match=match, force=True)
@@ -1492,7 +1494,7 @@
n = repo.commit(message, user, ph.date, match=match,
force=True)
# only write patch after a successful commit
- patchf.rename()
+ patchf.close()
self.applied.append(statusentry(n, patchfn))
except:
ctx = repo[cparents[0]]
@@ -2675,7 +2677,11 @@
return 0
@command("strip",
- [('f', 'force', None, _('force removal of changesets, discard '
+ [
+ ('r', 'rev', [], _('strip specified revision (optional, '
+ 'can specify revisions without this '
+ 'option)'), _('REV')),
+ ('f', 'force', None, _('force removal of changesets, discard '
'uncommitted changes (no backup)')),
('b', 'backup', None, _('bundle only changesets with local revision'
' number greater than REV which are not'
@@ -2716,6 +2722,7 @@
backup = 'none'
cl = repo.changelog
+ revs = list(revs) + opts.get('rev')
revs = set(scmutil.revrange(repo, revs))
if not revs:
raise util.Abort(_('empty revision set'))
@@ -2915,6 +2922,7 @@
@command("qqueue",
[('l', 'list', False, _('list all available queues')),
+ ('', 'active', False, _('print name of active queue')),
('c', 'create', False, _('create new queue')),
('', 'rename', False, _('rename active queue')),
('', 'delete', False, _('delete reference to queue')),
@@ -2929,7 +2937,8 @@
Omitting a queue name or specifying -l/--list will show you the registered
queues - by default the "normal" patches queue is registered. The currently
- active queue will be marked with "(active)".
+ active queue will be marked with "(active)". Specifying --active will print
+ only the name of the active queue.
To create a new queue, use -c/--create. The queue is automatically made
active, except in the case where there are applied patches from the
@@ -3022,8 +3031,11 @@
fh.close()
util.rename(repo.join('patches.queues.new'), repo.join(_allqueues))
- if not name or opts.get('list'):
+ if not name or opts.get('list') or opts.get('active'):
current = _getcurrent()
+ if opts.get('active'):
+ ui.write('%s\n' % (current,))
+ return
for queue in _getqueues():
ui.write('%s' % (queue,))
if queue == current and not ui.quiet:
--- a/hgext/notify.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/notify.py Wed Sep 28 16:11:53 2011 -0500
@@ -5,71 +5,115 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-'''hooks for sending email notifications at commit/push time
+'''hooks for sending email push notifications
-Subscriptions can be managed through a hgrc file. Default mode is to
-print messages to stdout, for testing and configuring.
+This extension let you run hooks sending email notifications when
+changesets are being pushed, from the sending or receiving side.
-To use, configure the notify extension and enable it in hgrc like
-this::
-
- [extensions]
- notify =
+First, enable the extension as explained in :hg:`help extensions`, and
+register the hook you want to run. ``incoming`` and ``outgoing`` hooks
+are run by the changesets receiver while the ``outgoing`` one is for
+the sender::
[hooks]
# one email for each incoming changeset
incoming.notify = python:hgext.notify.hook
- # batch emails when many changesets incoming at one time
+ # one email for all incoming changesets
changegroup.notify = python:hgext.notify.hook
- # batch emails when many changesets outgoing at one time (client side)
+
+ # one email for all outgoing changesets
outgoing.notify = python:hgext.notify.hook
- [notify]
- # config items go here
-
-Required configuration items::
-
- config = /path/to/file # file containing subscriptions
-
-Optional configuration items::
-
- test = True # print messages to stdout for testing
- strip = 3 # number of slashes to strip for url paths
- domain = example.com # domain to use if committer missing domain
- style = ... # style file to use when formatting email
- template = ... # template to use when formatting email
- incoming = ... # template to use when run as incoming hook
- outgoing = ... # template to use when run as outgoing hook
- changegroup = ... # template to use when run as changegroup hook
- maxdiff = 300 # max lines of diffs to include (0=none, -1=all)
- maxsubject = 67 # truncate subject line longer than this
- diffstat = True # add a diffstat before the diff content
- sources = serve # notify if source of incoming changes in this list
- # (serve == ssh or http, push, pull, bundle)
- merge = False # send notification for merges (default True)
- [email]
- from = user@host.com # email address to send as if none given
- [web]
- baseurl = http://hgserver/... # root of hg web site for browsing commits
-
-The notify config file has same format as a regular hgrc file. It has
-two sections so you can express subscriptions in whatever way is
-handier for you.
-
-::
+Now the hooks are running, subscribers must be assigned to
+repositories. Use the ``[usersubs]`` section to map repositories to a
+given email or the ``[reposubs]`` section to map emails to a single
+repository::
[usersubs]
- # key is subscriber email, value is ","-separated list of glob patterns
+ # key is subscriber email, value is a comma-separated list of glob
+ # patterns
user@host = pattern
[reposubs]
- # key is glob pattern, value is ","-separated list of subscriber emails
+ # key is glob pattern, value is a comma-separated list of subscriber
+ # emails
pattern = user@host
-Glob patterns are matched against path to repository root.
+Glob patterns are matched against absolute path to repository
+root. The subscriptions can be defined in their own file and
+referenced with::
+
+ [notify]
+ config = /path/to/subscriptionsfile
+
+Alternatively, they can be added to Mercurial configuration files by
+setting the previous entry to an empty value.
+
+At this point, notifications should be generated but will not be sent until you
+set the ``notify.test`` entry to ``False``.
+
+Notifications content can be tweaked with the following configuration entries:
+
+notify.test
+ If ``True``, print messages to stdout instead of sending them. Default: True.
+
+notify.sources
+ Space separated list of change sources. Notifications are sent only
+ if it includes the incoming or outgoing changes source. Incoming
+ sources can be ``serve`` for changes coming from http or ssh,
+ ``pull`` for pulled changes, ``unbundle`` for changes added by
+ :hg:`unbundle` or ``push`` for changes being pushed
+ locally. Outgoing sources are the same except for ``unbundle`` which
+ is replaced by ``bundle``. Default: serve.
+
+notify.strip
+ Number of leading slashes to strip from url paths. By default, notifications
+ references repositories with their absolute path. ``notify.strip`` let you
+ turn them into relative paths. For example, ``notify.strip=3`` will change
+ ``/long/path/repository`` into ``repository``. Default: 0.
+
+notify.domain
+ If subscribers emails or the from email have no domain set, complete them
+ with this value.
-If you like, you can put notify config file in repository that users
-can push changes to, they can manage their own subscriptions.
+notify.style
+ Style file to use when formatting emails.
+
+notify.template
+ Template to use when formatting emails.
+
+notify.incoming
+ Template to use when run as incoming hook, override ``notify.template``.
+
+notify.outgoing
+ Template to use when run as outgoing hook, override ``notify.template``.
+
+notify.changegroup
+ Template to use when running as changegroup hook, override
+ ``notify.template``.
+
+notify.maxdiff
+ Maximum number of diff lines to include in notification email. Set to 0
+ to disable the diff, -1 to include all of it. Default: 300.
+
+notify.maxsubject
+ Maximum number of characters in emails subject line. Default: 67.
+
+notify.diffstat
+ Set to True to include a diffstat before diff content. Default: True.
+
+notify.merge
+ If True, send notifications for merge changesets. Default: True.
+
+If set, the following entries will also be used to customize the notifications:
+
+email.from
+ Email ``From`` address to use if none can be found in generated email content.
+
+web.baseurl
+ Root repository browsing URL to combine with repository paths when making
+ references. See also ``notify.strip``.
+
'''
from mercurial.i18n import _
@@ -167,9 +211,6 @@
return [mail.addressencode(self.ui, s, self.charsets, self.test)
for s in sorted(subs)]
- def url(self, path=None):
- return self.ui.config('web', 'baseurl') + (path or self.root)
-
def node(self, ctx, **props):
'''format one changeset, unless it is a suppressed merge.'''
if not self.merge and len(ctx.parents()) > 1:
--- a/hgext/pager.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/pager.py Wed Sep 28 16:11:53 2011 -0500
@@ -58,7 +58,7 @@
from mercurial.i18n import _
def _runpager(p):
- if not hasattr(os, 'fork'):
+ if not util.safehasattr(os, 'fork'):
sys.stdout = util.popen(p, 'wb')
if util.isatty(sys.stderr):
sys.stderr = sys.stdout
--- a/hgext/patchbomb.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/patchbomb.py Wed Sep 28 16:11:53 2011 -0500
@@ -57,24 +57,15 @@
command = cmdutil.command(cmdtable)
def prompt(ui, prompt, default=None, rest=':'):
- if not ui.interactive() and default is None:
- raise util.Abort(_("%s Please enter a valid value" % (prompt + rest)))
if default:
prompt += ' [%s]' % default
- prompt += rest
- while True:
- r = ui.prompt(prompt, default=default)
- if r:
- return r
- if default is not None:
- return default
- ui.warn(_('Please enter a valid value.\n'))
+ return ui.prompt(prompt + rest, default)
-def introneeded(opts, number):
- '''is an introductory message required?'''
+def introwanted(opts, number):
+ '''is an introductory message apparently wanted?'''
return number > 1 or opts.get('intro') or opts.get('desc')
-def makepatch(ui, repo, patchlines, opts, _charsets, idx, total,
+def makepatch(ui, repo, patchlines, opts, _charsets, idx, total, numbered,
patchname=None):
desc = []
@@ -141,7 +132,7 @@
flag = ' ' + flag
subj = desc[0].strip().rstrip('. ')
- if not introneeded(opts, total):
+ if not numbered:
subj = '[PATCH%s] %s' % (flag, opts.get('subject') or subj)
else:
tlen = len(str(total))
@@ -352,51 +343,66 @@
ui.write(_('\nWrite the introductory message for the '
'patch series.\n\n'))
body = ui.edit(body, sender)
- # Save serie description in case sendmail fails
+ # Save series description in case sendmail fails
msgfile = repo.opener('last-email.txt', 'wb')
msgfile.write(body)
msgfile.close()
return body
def getpatchmsgs(patches, patchnames=None):
- jumbo = []
msgs = []
ui.write(_('This patch series consists of %d patches.\n\n')
% len(patches))
+ # build the intro message, or skip it if the user declines
+ if introwanted(opts, len(patches)):
+ msg = makeintro(patches)
+ if msg:
+ msgs.append(msg)
+
+ # are we going to send more than one message?
+ numbered = len(msgs) + len(patches) > 1
+
+ # now generate the actual patch messages
name = None
for i, p in enumerate(patches):
- jumbo.extend(p)
if patchnames:
name = patchnames[i]
msg = makepatch(ui, repo, p, opts, _charsets, i + 1,
- len(patches), name)
+ len(patches), numbered, name)
msgs.append(msg)
- if introneeded(opts, len(patches)):
- tlen = len(str(len(patches)))
+ return msgs
+
+ def makeintro(patches):
+ tlen = len(str(len(patches)))
- flag = ' '.join(opts.get('flag'))
- if flag:
- subj = '[PATCH %0*d of %d %s]' % (tlen, 0, len(patches), flag)
- else:
- subj = '[PATCH %0*d of %d]' % (tlen, 0, len(patches))
- subj += ' ' + (opts.get('subject') or
- prompt(ui, 'Subject: ', rest=subj))
+ flag = opts.get('flag') or ''
+ if flag:
+ flag = ' ' + ' '.join(flag)
+ prefix = '[PATCH %0*d of %d%s]' % (tlen, 0, len(patches), flag)
+
+ subj = (opts.get('subject') or
+ prompt(ui, 'Subject: ', rest=prefix, default=''))
+ if not subj:
+ return None # skip intro if the user doesn't bother
- body = ''
- ds = patch.diffstat(jumbo)
- if ds and opts.get('diffstat'):
- body = '\n' + ds
+ subj = prefix + ' ' + subj
- body = getdescription(body, sender)
- msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
- msg['Subject'] = mail.headencode(ui, subj, _charsets,
- opts.get('test'))
+ body = ''
+ if opts.get('diffstat'):
+ # generate a cumulative diffstat of the whole patch series
+ diffstat = patch.diffstat(sum(patches, []))
+ body = '\n' + diffstat
+ else:
+ diffstat = None
- msgs.insert(0, (msg, subj, ds))
- return msgs
+ body = getdescription(body, sender)
+ msg = mail.mimeencode(ui, body, _charsets, opts.get('test'))
+ msg['Subject'] = mail.headencode(ui, subj, _charsets,
+ opts.get('test'))
+ return (msg, subj, diffstat)
def getbundlemsgs(bundle):
subj = (opts.get('subject')
@@ -429,29 +435,33 @@
showaddrs = []
- def getaddrs(opt, prpt=None, default=None):
- addrs = opts.get(opt.replace('-', '_'))
- if opt != 'reply-to':
- showaddr = '%s:' % opt.capitalize()
- else:
- showaddr = 'Reply-To:'
-
+ def getaddrs(header, ask=False, default=None):
+ configkey = header.lower()
+ opt = header.replace('-', '_').lower()
+ addrs = opts.get(opt)
if addrs:
- showaddrs.append('%s %s' % (showaddr, ', '.join(addrs)))
+ showaddrs.append('%s: %s' % (header, ', '.join(addrs)))
return mail.addrlistencode(ui, addrs, _charsets, opts.get('test'))
- addrs = ui.config('email', opt) or ui.config('patchbomb', opt) or ''
- if not addrs and prpt:
- addrs = prompt(ui, prpt, default)
+ # not on the command line: fallback to config and then maybe ask
+ addr = (ui.config('email', configkey) or
+ ui.config('patchbomb', configkey) or
+ '')
+ if not addr and ask:
+ addr = prompt(ui, header, default=default)
+ if addr:
+ showaddrs.append('%s: %s' % (header, addr))
+ return mail.addrlistencode(ui, [addr], _charsets, opts.get('test'))
+ else:
+ return default
- if addrs:
- showaddrs.append('%s %s' % (showaddr, addrs))
- return mail.addrlistencode(ui, [addrs], _charsets, opts.get('test'))
-
- to = getaddrs('to', 'To')
- cc = getaddrs('cc', 'Cc', '')
- bcc = getaddrs('bcc')
- replyto = getaddrs('reply-to')
+ to = getaddrs('To', ask=True)
+ if not to:
+ # we can get here in non-interactive mode
+ raise util.Abort(_('no recipient addresses provided'))
+ cc = getaddrs('Cc', ask=True, default='') or []
+ bcc = getaddrs('Bcc') or []
+ replyto = getaddrs('Reply-To')
if opts.get('diffstat') or opts.get('confirm'):
ui.write(_('\nFinal summary:\n\n'))
--- a/hgext/progress.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/progress.py Wed Sep 28 16:11:53 2011 -0500
@@ -27,6 +27,9 @@
[progress]
delay = 3 # number of seconds (float) before showing the progress bar
+ changedelay = 1 # changedelay: minimum delay before showing a new topic.
+ # If set to less than 3 * refresh, that value will
+ # be used instead.
refresh = 0.1 # time in seconds between refreshes of the progress bar
format = topic bar number estimate # format of the progress bar
width = <none> # if set, the maximum width of the progress information
@@ -53,7 +56,7 @@
return ' '.join(s for s in args if s)
def shouldprint(ui):
- return (util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty'))
+ return util.isatty(sys.stderr) or ui.configbool('progress', 'assume-tty')
def fmtremaining(seconds):
if seconds < 60:
@@ -105,9 +108,13 @@
self.printed = False
self.lastprint = time.time() + float(self.ui.config(
'progress', 'delay', default=3))
+ self.lasttopic = None
self.indetcount = 0
self.refresh = float(self.ui.config(
'progress', 'refresh', default=0.1))
+ self.changedelay = max(3 * self.refresh,
+ float(self.ui.config(
+ 'progress', 'changedelay', default=1)))
self.order = self.ui.configlist(
'progress', 'format',
default=['topic', 'bar', 'number', 'estimate'])
@@ -184,6 +191,7 @@
else:
out = spacejoin(head, tail)
sys.stderr.write('\r' + out[:termwidth])
+ self.lasttopic = topic
sys.stderr.flush()
def clear(self):
@@ -248,10 +256,18 @@
self.topics.append(topic)
self.topicstates[topic] = pos, item, unit, total
if now - self.lastprint >= self.refresh and self.topics:
- self.lastprint = now
- self.show(now, topic, *self.topicstates[topic])
+ if (self.lasttopic is None # first time we printed
+ # not a topic change
+ or topic == self.lasttopic
+ # it's been long enough we should print anyway
+ or now - self.lastprint >= self.changedelay):
+ self.lastprint = now
+ self.show(now, topic, *self.topicstates[topic])
+
+_singleton = None
def uisetup(ui):
+ global _singleton
class progressui(ui.__class__):
_progbar = None
@@ -278,7 +294,9 @@
# we instantiate one globally shared progress bar to avoid
# competing progress bars when multiple UI objects get created
if not progressui._progbar:
- progressui._progbar = progbar(ui)
+ if _singleton is None:
+ _singleton = progbar(ui)
+ progressui._progbar = _singleton
def reposetup(ui, repo):
uisetup(repo.ui)
--- a/hgext/rebase.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/rebase.py Wed Sep 28 16:11:53 2011 -0500
@@ -536,11 +536,14 @@
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 samebranch and commonbase == repo[dest]:
- raise util.Abort(_('source is descendant of destination'))
+ if commonbase == repo[dest]:
+ samebranch = repo[src].branch() == repo[dest].branch()
+ if samebranch and repo[src] in repo[dest].children():
+ raise util.Abort(_('source is a child of destination'))
+ # rebase on ancestor, force detach
+ detach = True
source = repo[src].rev()
if detach:
# We need to keep track of source's ancestors up to the common base
--- a/hgext/relink.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/relink.py Wed Sep 28 16:11:53 2011 -0500
@@ -36,7 +36,8 @@
command is running. (Both repositories will be locked against
writes.)
"""
- if not hasattr(util, 'samefile') or not hasattr(util, 'samedevice'):
+ if (not util.safehasattr(util, 'samefile') or
+ not util.safehasattr(util, 'samedevice')):
raise util.Abort(_('hardlinks are not supported on this system'))
src = hg.repository(ui, ui.expandpath(origin or 'default-relink',
origin or 'default'))
--- a/hgext/share.py Tue Sep 20 15:21:27 2011 +0300
+++ b/hgext/share.py Wed Sep 28 16:11:53 2011 -0500
@@ -6,7 +6,7 @@
'''share a common history between several working directories'''
from mercurial.i18n import _
-from mercurial import hg, commands
+from mercurial import hg, commands, util
def share(ui, source, dest=None, noupdate=False):
"""create a new shared repository
@@ -28,11 +28,46 @@
return hg.share(ui, source, dest, not noupdate)
+def unshare(ui, repo):
+ """convert a shared repository to a normal one
+
+ Copy the store data to the repo and remove the sharedpath data.
+ """
+
+ if repo.sharedpath == repo.path:
+ raise util.Abort(_("this is not a shared repo"))
+
+ destlock = lock = None
+ lock = repo.lock()
+ try:
+ # we use locks here because if we race with commit, we
+ # can end up with extra data in the cloned revlogs that's
+ # not pointed to by changesets, thus causing verify to
+ # fail
+
+ destlock = hg.copystore(ui, repo, repo.path)
+
+ sharefile = repo.join('sharedpath')
+ util.rename(sharefile, sharefile + '.old')
+
+ repo.requirements.discard('sharedpath')
+ repo._writerequirements()
+ finally:
+ destlock and destlock.release()
+ lock and lock.release()
+
+ # update store, spath, sopener and sjoin of repo
+ repo.__init__(ui, repo.root)
+
cmdtable = {
"share":
(share,
[('U', 'noupdate', None, _('do not create a working copy'))],
_('[-U] SOURCE [DEST]')),
+ "unshare":
+ (unshare,
+ [],
+ ''),
}
commands.norepo += " share"
--- a/mercurial/archival.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/archival.py Wed Sep 28 16:11:53 2011 -0500
@@ -195,7 +195,7 @@
return
f = self.opener(name, "w", atomictemp=True)
f.write(data)
- f.rename()
+ f.close()
destfile = os.path.join(self.basedir, name)
os.chmod(destfile, mode)
--- a/mercurial/bookmarks.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/bookmarks.py Wed Sep 28 16:11:53 2011 -0500
@@ -26,7 +26,13 @@
bookmarks = {}
try:
for line in repo.opener('bookmarks'):
- sha, refspec = line.strip().split(' ', 1)
+ line = line.strip()
+ if not line:
+ continue
+ if ' ' not in line:
+ repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
+ continue
+ sha, refspec = line.split(' ', 1)
refspec = encoding.tolocal(refspec)
try:
bookmarks[refspec] = repo.changelog.lookup(sha)
@@ -84,7 +90,7 @@
file = repo.opener('bookmarks', 'w', atomictemp=True)
for refspec, node in refs.iteritems():
file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
- file.rename()
+ file.close()
# touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
try:
@@ -115,7 +121,7 @@
try:
file = repo.opener('bookmarks.current', 'w', atomictemp=True)
file.write(encoding.fromlocal(mark))
- file.rename()
+ file.close()
finally:
wlock.release()
repo._bookmarkcurrent = mark
@@ -145,11 +151,10 @@
def listbookmarks(repo):
# We may try to list bookmarks on a repo type that does not
# support it (e.g., statichttprepository).
- if not hasattr(repo, '_bookmarks'):
- return {}
+ marks = getattr(repo, '_bookmarks', {})
d = {}
- for k, v in repo._bookmarks.iteritems():
+ for k, v in marks.iteritems():
d[k] = hex(v)
return d
--- a/mercurial/byterange.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/byterange.py Wed Sep 28 16:11:53 2011 -0500
@@ -103,9 +103,7 @@
"""This effectively allows us to wrap at the instance level.
Any attribute not found in _this_ object will be searched for
in self.fo. This includes methods."""
- if hasattr(self.fo, name):
- return getattr(self.fo, name)
- raise AttributeError(name)
+ return getattr(self.fo, name)
def tell(self):
"""Return the position within the range.
@@ -170,10 +168,8 @@
offset is relative to the current position (self.realpos).
"""
assert offset >= 0
- if not hasattr(self.fo, 'seek'):
- self._poor_mans_seek(offset)
- else:
- self.fo.seek(self.realpos + offset)
+ seek = getattr(self.fo, 'seek', self._poor_mans_seek)
+ seek(self.realpos + offset)
self.realpos += offset
def _poor_mans_seek(self, offset):
--- a/mercurial/cmdutil.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/cmdutil.py Wed Sep 28 16:11:53 2011 -0500
@@ -109,12 +109,13 @@
limit = None
return limit
-def makefilename(repo, pat, node,
+def makefilename(repo, pat, node, desc=None,
total=None, seqno=None, revwidth=None, pathname=None):
node_expander = {
'H': lambda: hex(node),
'R': lambda: str(repo.changelog.rev(node)),
'h': lambda: short(node),
+ 'm': lambda: re.sub('[^\w]', '_', str(desc))
}
expander = {
'%': lambda: '%',
@@ -154,14 +155,14 @@
raise util.Abort(_("invalid format spec '%%%s' in output filename") %
inst.args[0])
-def makefileobj(repo, pat, node=None, total=None,
+def makefileobj(repo, pat, node=None, desc=None, total=None,
seqno=None, revwidth=None, mode='wb', pathname=None):
writable = mode not in ('r', 'rb')
if not pat or pat == '-':
fp = writable and repo.ui.fout or repo.ui.fin
- if hasattr(fp, 'fileno'):
+ if util.safehasattr(fp, 'fileno'):
return os.fdopen(os.dup(fp.fileno()), mode)
else:
# if this fp can't be duped properly, return
@@ -177,11 +178,11 @@
return getattr(self.f, attr)
return wrappedfileobj(fp)
- if hasattr(pat, 'write') and writable:
+ if util.safehasattr(pat, 'write') and writable:
return pat
- if hasattr(pat, 'read') and 'r' in mode:
+ if util.safehasattr(pat, 'read') and 'r' in mode:
return pat
- return open(makefilename(repo, pat, node, total, seqno, revwidth,
+ return open(makefilename(repo, pat, node, desc, total, seqno, revwidth,
pathname),
mode)
@@ -516,11 +517,13 @@
shouldclose = False
if not fp:
- fp = makefileobj(repo, template, node, total=total, seqno=seqno,
- revwidth=revwidth, mode='ab')
+ desc_lines = ctx.description().rstrip().split('\n')
+ desc = desc_lines[0] #Commit always has a first line.
+ fp = makefileobj(repo, template, node, desc=desc, total=total,
+ seqno=seqno, revwidth=revwidth, mode='ab')
if fp != template:
shouldclose = True
- if fp != sys.stdout and hasattr(fp, 'name'):
+ if fp != sys.stdout and util.safehasattr(fp, 'name'):
repo.ui.note("%s\n" % fp.name)
fp.write("# HG changeset patch\n")
--- a/mercurial/commands.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/commands.py Wed Sep 28 16:11:53 2011 -0500
@@ -119,6 +119,10 @@
('', 'stat', None, _('output diffstat-style summary of changes')),
]
+mergetoolopts = [
+ ('t', 'tool', '', _('specify merge tool')),
+]
+
similarityopts = [
('s', 'similarity', '',
_('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
@@ -303,6 +307,18 @@
The archive type is automatically detected based on file
extension (or override using -t/--type).
+ .. container:: verbose
+
+ Examples:
+
+ - create a zip file containing the 1.0 release::
+
+ hg archive -r 1.0 project-1.0.zip
+
+ - create a tarball excluding .hg files::
+
+ hg archive project.tar.gz -X ".hg*"
+
Valid types are:
:``files``: a directory full of files (default)
@@ -349,9 +365,8 @@
@command('backout',
[('', 'merge', None, _('merge with old dirstate parent after backout')),
('', 'parent', '', _('parent to choose when backing out merge'), _('REV')),
- ('t', 'tool', '', _('specify merge tool')),
('r', 'rev', '', _('revision to backout'), _('REV')),
- ] + walkopts + commitopts + commitopts2,
+ ] + mergetoolopts + walkopts + commitopts + commitopts2,
_('[OPTION]... [-r] REV'))
def backout(ui, repo, node=None, rev=None, **opts):
'''reverse effect of earlier changeset
@@ -486,6 +501,54 @@
(command not found) will abort the bisection, and any other
non-zero exit status means the revision is bad.
+ .. container:: verbose
+
+ Some examples:
+
+ - start a bisection with known bad revision 12, and good revision 34::
+
+ hg bisect --bad 34
+ hg bisect --good 12
+
+ - advance the current bisection by marking current revision as good or
+ bad::
+
+ hg bisect --good
+ hg bisect --bad
+
+ - mark the current revision, or a known revision, to be skipped (eg. if
+ that revision is not usable because of another issue)::
+
+ hg bisect --skip
+ hg bisect --skip 23
+
+ - forget the current bisection::
+
+ hg bisect --reset
+
+ - use 'make && make tests' to automatically find the first broken
+ revision::
+
+ hg bisect --reset
+ hg bisect --bad 34
+ hg bisect --good 12
+ hg bisect --command 'make && make tests'
+
+ - see all changesets whose states are already known in the current
+ bisection::
+
+ hg log -r "bisect(pruned)"
+
+ - see all changesets that took part in the current bisection::
+
+ hg log -r "bisect(range)"
+
+ - with the graphlog extension, you can even get a nice graph::
+
+ hg log --graph -r "bisect(range)"
+
+ See :hg:`help revsets` for more about the `bisect()` keyword.
+
Returns 0 on success.
"""
def extendbisectrange(nodes, good):
@@ -1102,8 +1165,8 @@
ctx = repo[node]
parents = ctx.parents()
- if bheads and not [x for x in parents
- if x.node() in bheads and x.branch() == branch]:
+ if (bheads and node not in bheads and not
+ [x for x in parents if x.node() in bheads and x.branch() == branch]):
ui.status(_('created new head\n'))
# The message is not printed for initial roots. For the other
# changesets, it is printed in the following situations:
@@ -1656,8 +1719,9 @@
def debugignore(ui, repo, *values, **opts):
"""display the combined ignore pattern"""
ignore = repo.dirstate._ignore
- if hasattr(ignore, 'includepat'):
- ui.write("%s\n" % ignore.includepat)
+ includepat = getattr(ignore, 'includepat', None)
+ if includepat is not None:
+ ui.write("%s\n" % includepat)
else:
raise util.Abort(_("no ignore patterns found"))
@@ -2170,6 +2234,32 @@
Use the -g/--git option to generate diffs in the git extended diff
format. For more information, read :hg:`help diffs`.
+ .. container:: verbose
+
+ Examples:
+
+ - compare a file in the current working directory to its parent::
+
+ hg diff foo.c
+
+ - compare two historical versions of a directory, with rename info::
+
+ hg diff --git -r 1.0:1.2 lib/
+
+ - get change stats relative to the last change on some date::
+
+ hg diff --stat -r "date('may 2')"
+
+ - diff all newly-added files that contain a keyword::
+
+ hg diff "set:added() and grep(GNU)"
+
+ - compare a revision and its parents::
+
+ hg diff -c 9353 # compare against first parent
+ hg diff -r 9353^:9353 # same using revset syntax
+ hg diff -r 9353^2:9353 # compare against the second parent
+
Returns 0 on success.
"""
@@ -2225,6 +2315,7 @@
:``%R``: changeset revision number
:``%b``: basename of the exporting repository
:``%h``: short-form changeset hash (12 hexadecimal digits)
+ :``%m``: first line of the commit message (only alphanumeric characters)
:``%n``: zero-padded sequence number, starting at 1
:``%r``: zero-padded changeset revision number
@@ -2238,6 +2329,25 @@
With the --switch-parent option, the diff will be against the
second parent. It can be useful to review a merge.
+ .. container:: verbose
+
+ Examples:
+
+ - use export and import to transplant a bugfix to the current
+ branch::
+
+ hg export -r 9353 | hg import -
+
+ - export all the changesets between two revisions to a file with
+ rename information::
+
+ hg export --git -r 123:150 > changes.txt
+
+ - split outgoing changes into a series of patches with
+ descriptive names::
+
+ hg export -r "outgoing()" -o "%n-%m.patch"
+
Returns 0 on success.
"""
changesets += tuple(opts.get('rev', []))
@@ -2265,6 +2375,18 @@
To undo a forget before the next commit, see :hg:`add`.
+ .. container:: verbose
+
+ Examples:
+
+ - forget newly-added binary files::
+
+ hg forget "set:added() and binary()"
+
+ - forget files that would be excluded by .hgignore::
+
+ hg forget "set:hgignore()"
+
Returns 0 on success.
"""
@@ -2576,7 +2698,7 @@
[('e', 'extension', None, _('show only help for extensions')),
('c', 'command', None, _('show only help for commands'))],
_('[-ec] [TOPIC]'))
-def help_(ui, name=None, with_version=False, unknowncmd=False, full=True, **opts):
+def help_(ui, name=None, unknowncmd=False, full=True, **opts):
"""show help for a given topic or a help overview
With no arguments, print a list of commands with short help messages.
@@ -2586,14 +2708,64 @@
Returns 0 if successful.
"""
- option_lists = []
+
textwidth = min(ui.termwidth(), 80) - 2
- def addglobalopts(aliases):
+ def optrst(options):
+ data = []
+ multioccur = False
+ for option in options:
+ if len(option) == 5:
+ shortopt, longopt, default, desc, optlabel = option
+ else:
+ shortopt, longopt, default, desc = option
+ optlabel = _("VALUE") # default label
+
+ if _("DEPRECATED") in desc and not ui.verbose:
+ continue
+
+ so = ''
+ if shortopt:
+ so = '-' + shortopt
+ lo = '--' + longopt
+ if default:
+ desc += _(" (default: %s)") % default
+
+ if isinstance(default, list):
+ lo += " %s [+]" % optlabel
+ multioccur = True
+ elif (default is not None) and not isinstance(default, bool):
+ lo += " %s" % optlabel
+
+ data.append((so, lo, desc))
+
+ rst = minirst.maketable(data, 1)
+ if multioccur:
+ rst += _("\n[+] marked option can be specified multiple times")
+
+ return rst
+
+ # list all option lists
+ def opttext(optlist, width):
+ rst = ''
+ if not optlist:
+ return ''
+
+ for title, options in optlist:
+ rst += '\n%s\n\n' % title
+ rst += optrst(options)
+ rst += '\n'
+
+ return '\n' + minirst.format(rst, width)
+
+ def addglobalopts(optlist, aliases):
+ if ui.quiet:
+ return []
+
if ui.verbose:
- option_lists.append((_("global options:"), globalopts))
+ optlist.append((_("global options:"), globalopts))
if name == 'shortlist':
- option_lists.append((_('use "hg help" for the full list '
+ optlist.append((_('use "hg help" for the full list '
'of commands'), ()))
else:
if name == 'shortlist':
@@ -2606,13 +2778,10 @@
'global options') % (name and " " + name or "")
else:
msg = _('use "hg -v help %s" to show global options') % name
- option_lists.append((msg, ()))
+ optlist.append((msg, ()))
def helpcmd(name):
- if with_version:
- version_(ui)
- ui.write('\n')
-
+ optlist = []
try:
aliases, entry = cmdutil.findcmd(name, table, strict=unknowncmd)
except error.AmbiguousCommand, inst:
@@ -2620,7 +2789,7 @@
# except block, nor can be used inside a lambda. python issue4617
prefix = inst.args[0]
select = lambda c: c.lstrip('^').startswith(prefix)
- helplist(_('list of commands:\n\n'), select)
+ helplist(select)
return
# check if it's an invalid alias and display its error if it is
@@ -2646,7 +2815,7 @@
doc = gettext(entry[0].__doc__)
if not doc:
doc = _("(no help text available)")
- if hasattr(entry[0], 'definition'): # aliased command
+ if util.safehasattr(entry[0], 'definition'): # aliased command
if entry[0].definition.startswith('!'): # shell alias
doc = _('shell alias for::\n\n %s') % entry[0].definition[1:]
else:
@@ -2655,16 +2824,14 @@
doc = doc.splitlines()[0]
keep = ui.verbose and ['verbose'] or []
formatted, pruned = minirst.format(doc, textwidth, keep=keep)
- ui.write("\n%s\n" % formatted)
+ ui.write("\n%s" % formatted)
if pruned:
ui.write(_('\nuse "hg -v help %s" to show verbose help\n') % name)
if not ui.quiet:
# options
if entry[1]:
- option_lists.append((_("options:\n"), entry[1]))
-
- addglobalopts(False)
+ optlist.append((_("options:\n"), entry[1]))
# check if this command shadows a non-trivial (multi-line)
# extension help text
@@ -2678,7 +2845,16 @@
except KeyError:
pass
- def helplist(header, select=None):
+ addglobalopts(optlist, False)
+ ui.write(opttext(optlist, textwidth))
+
+ def helplist(select=None):
+ # list of commands
+ if name == "shortlist":
+ header = _('basic commands:\n\n')
+ else:
+ header = _('list of commands:\n\n')
+
h = {}
cmds = {}
for c, e in table.iteritems():
@@ -2718,8 +2894,22 @@
initindent=' %-*s ' % (m, f),
hangindent=' ' * (m + 4))))
- if not ui.quiet:
- addglobalopts(True)
+ if not name:
+ text = help.listexts(_('enabled extensions:'), extensions.enabled())
+ if text:
+ ui.write("\n%s" % minirst.format(text, textwidth))
+
+ ui.write(_("\nadditional help topics:\n\n"))
+ topics = []
+ for names, header, doc in help.helptable:
+ topics.append((sorted(names, key=len, reverse=True)[0], header))
+ topics_len = max([len(s[0]) for s in topics])
+ for t, desc in topics:
+ ui.write(" %-*s %s\n" % (topics_len, t, desc))
+
+ optlist = []
+ addglobalopts(optlist, True)
+ ui.write(opttext(optlist, textwidth))
def helptopic(name):
for names, header, doc in help.helptable:
@@ -2731,11 +2921,11 @@
# description
if not doc:
doc = _("(no help text available)")
- if hasattr(doc, '__call__'):
+ if util.safehasattr(doc, '__call__'):
doc = doc()
ui.write("%s\n\n" % header)
- ui.write("%s\n" % minirst.format(doc, textwidth, indent=4))
+ ui.write("%s" % minirst.format(doc, textwidth, indent=4))
try:
cmdutil.findcmd(name, table)
ui.write(_('\nuse "hg help -c %s" to see help for '
@@ -2760,7 +2950,7 @@
ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
if tail:
ui.write(minirst.format(tail, textwidth))
- ui.status('\n\n')
+ ui.status('\n')
if mod:
try:
@@ -2768,7 +2958,7 @@
except AttributeError:
ct = {}
modcmds = set([c.split('|', 1)[0] for c in ct])
- helplist(_('list of commands:\n\n'), modcmds.__contains__)
+ helplist(modcmds.__contains__)
else:
ui.write(_('use "hg help extensions" for information on enabling '
'extensions\n'))
@@ -2780,7 +2970,7 @@
msg = help.listexts(_("'%s' is provided by the following "
"extension:") % cmd, {ext: doc}, indent=4)
ui.write(minirst.format(msg, textwidth))
- ui.write('\n\n')
+ ui.write('\n')
ui.write(_('use "hg help extensions" for information on enabling '
'extensions\n'))
@@ -2803,87 +2993,12 @@
i = inst
if i:
raise i
-
else:
# program name
- if ui.verbose or with_version:
- version_(ui)
- else:
- ui.status(_("Mercurial Distributed SCM\n"))
+ ui.status(_("Mercurial Distributed SCM\n"))
ui.status('\n')
-
- # list of commands
- if name == "shortlist":
- header = _('basic commands:\n\n')
- else:
- header = _('list of commands:\n\n')
-
- helplist(header)
- if name != 'shortlist':
- text = help.listexts(_('enabled extensions:'), extensions.enabled())
- if text:
- ui.write("\n%s\n" % minirst.format(text, textwidth))
-
- # list all option lists
- opt_output = []
- multioccur = False
- for title, options in option_lists:
- opt_output.append(("\n%s" % title, None))
- for option in options:
- if len(option) == 5:
- shortopt, longopt, default, desc, optlabel = option
- else:
- shortopt, longopt, default, desc = option
- optlabel = _("VALUE") # default label
-
- if _("DEPRECATED") in desc and not ui.verbose:
- continue
- if isinstance(default, list):
- numqualifier = " %s [+]" % optlabel
- multioccur = True
- elif (default is not None) and not isinstance(default, bool):
- numqualifier = " %s" % optlabel
- else:
- numqualifier = ""
- opt_output.append(("%2s%s" %
- (shortopt and "-%s" % shortopt,
- longopt and " --%s%s" %
- (longopt, numqualifier)),
- "%s%s" % (desc,
- default
- and _(" (default: %s)") % default
- or "")))
- if multioccur:
- msg = _("\n[+] marked option can be specified multiple times")
- if ui.verbose and name != 'shortlist':
- opt_output.append((msg, None))
- else:
- opt_output.insert(-1, (msg, None))
-
- if not name:
- ui.write(_("\nadditional help topics:\n\n"))
- topics = []
- for names, header, doc in help.helptable:
- topics.append((sorted(names, key=len, reverse=True)[0], header))
- topics_len = max([len(s[0]) for s in topics])
- for t, desc in topics:
- ui.write(" %-*s %s\n" % (topics_len, t, desc))
-
- if opt_output:
- colwidth = encoding.colwidth
- # normalize: (opt or message, desc or None, width of opt)
- entries = [desc and (opt, desc, colwidth(opt)) or (opt, None, 0)
- for opt, desc in opt_output]
- hanging = max([e[2] for e in entries])
- for opt, desc, width in entries:
- if desc:
- initindent = ' %s%s ' % (opt, ' ' * (hanging - width))
- hangindent = ' ' * (hanging + 3)
- ui.write('%s\n' % (util.wrap(desc, textwidth,
- initindent=initindent,
- hangindent=hangindent)))
- else:
- ui.write("%s\n" % opt)
+ helplist()
+
@command('identify|id',
[('r', 'rev', '',
@@ -2909,6 +3024,22 @@
Specifying a path to a repository root or Mercurial bundle will
cause lookup to operate on that repository/bundle.
+ .. container:: verbose
+
+ Examples:
+
+ - generate a build identifier for the working directory::
+
+ hg id --id > build-id.dat
+
+ - find the revision corresponding to a tag::
+
+ hg id -n -r 1.3
+
+ - check the most recent revision of a remote repository::
+
+ hg id -r tip http://selenic.com/hg/
+
Returns 0 if successful.
"""
@@ -3057,6 +3188,27 @@
a URL is specified, the patch will be downloaded from it.
See :hg:`help dates` for a list of formats valid for -d/--date.
+ .. container:: verbose
+
+ Examples:
+
+ - import a traditional patch from a website and detect renames::
+
+ hg import -s 80 http://example.com/bugfix.patch
+
+ - import a changeset from an hgweb server::
+
+ hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
+
+ - import all the patches in an Unix-style mbox::
+
+ hg import incoming-patches.mbox
+
+ - attempt to exactly restore an exported changeset (not always
+ possible)::
+
+ hg import --exact proposed-fix.patch
+
Returns 0 on success.
"""
patches = (patch1,) + patches
@@ -3356,18 +3508,14 @@
Print the revision history of the specified files or the entire
project.
+ If no revision range is specified, the default is ``tip:0`` unless
+ --follow is set, in which case the working directory parent is
+ used as the starting revision.
+
File history is shown without following rename or copy history of
files. Use -f/--follow with a filename to follow history across
renames and copies. --follow without a filename will only show
- ancestors or descendants of the starting revision. --follow-first
- only follows the first parent of merge revisions.
-
- If no revision range is specified, the default is ``tip:0`` unless
- --follow is set, in which case the working directory parent is
- used as the starting revision. You can specify a revision set for
- log, see :hg:`help revsets` for more information.
-
- See :hg:`help dates` for a list of formats valid for -d/--date.
+ ancestors or descendants of the starting revision.
By default this command prints revision number and changeset id,
tags, non-trivial parents, user, date and time, and a summary for
@@ -3380,6 +3528,57 @@
its first parent. Also, only files different from BOTH parents
will appear in files:.
+ .. note::
+ for performance reasons, log FILE may omit duplicate changes
+ made on branches and will not show deletions. To see all
+ changes including duplicates and deletions, use the --removed
+ switch.
+
+ .. container:: verbose
+
+ Some examples:
+
+ - changesets with full descriptions and file lists::
+
+ hg log -v
+
+ - changesets ancestral to the working directory::
+
+ hg log -f
+
+ - last 10 commits on the current branch::
+
+ hg log -l 10 -b .
+
+ - changesets showing all modifications of a file, including removals::
+
+ hg log --removed file.c
+
+ - all changesets that touch a directory, with diffs, excluding merges::
+
+ hg log -Mp lib/
+
+ - all revision numbers that match a keyword::
+
+ hg log -k bug --template "{rev}\\n"
+
+ - check if a given changeset is included is a tagged release::
+
+ hg log -r "a21ccf and ancestor(1.9)"
+
+ - find all changesets by some user in a date range::
+
+ hg log -k alice -d "may 2008 to jul 2008"
+
+ - summary of all changesets after the last tag::
+
+ hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
+
+ See :hg:`help dates` for a list of formats valid for -d/--date.
+
+ See :hg:`help revisions` and :hg:`help revsets` for more about
+ specifying revisions.
+
Returns 0 on success.
"""
@@ -3507,10 +3706,10 @@
@command('^merge',
[('f', 'force', None, _('force a merge with outstanding changes')),
- ('t', 'tool', '', _('specify merge tool')),
('r', 'rev', '', _('revision to merge'), _('REV')),
('P', 'preview', None,
- _('review revisions to merge (no merge is performed)'))],
+ _('review revisions to merge (no merge is performed)'))
+ ] + mergetoolopts,
_('[-P] [-f] [[-r] REV]'))
def merge(ui, repo, node=None, **opts):
"""merge working directory with another revision
@@ -3589,7 +3788,7 @@
try:
# ui.forcemerge is an internal variable, do not document
- ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
+ repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''))
return hg.merge(repo, node, force=opts.get('force'))
finally:
ui.setconfig('ui', 'forcemerge', '')
@@ -3935,31 +4134,36 @@
def remove(ui, repo, *pats, **opts):
"""remove the specified files on the next commit
- Schedule the indicated files for removal from the repository.
-
- This only removes files from the current branch, not from the
- entire project history. -A/--after can be used to remove only
- files that have already been deleted, -f/--force can be used to
- force deletion, and -Af can be used to remove files from the next
- revision without deleting them from the working directory.
-
- The following table details the behavior of remove for different
- file states (columns) and option combinations (rows). The file
- states are Added [A], Clean [C], Modified [M] and Missing [!] (as
- reported by :hg:`status`). The actions are Warn, Remove (from
- branch) and Delete (from disk)::
-
- A C M !
- none W RD W R
- -f R RD RD R
- -A W W W R
- -Af R R R R
-
- Note that remove never deletes files in Added [A] state from the
- working directory, not even if option --force is specified.
+ Schedule the indicated files for removal from the current branch.
This command schedules the files to be removed at the next commit.
- To undo a remove before that, see :hg:`revert`.
+ To undo a remove before that, see :hg:`revert`. To undo added
+ files, see :hg:`forget`.
+
+ .. container:: verbose
+
+ -A/--after can be used to remove only files that have already
+ been deleted, -f/--force can be used to force deletion, and -Af
+ can be used to remove files from the next revision without
+ deleting them from the working directory.
+
+ The following table details the behavior of remove for different
+ file states (columns) and option combinations (rows). The file
+ states are Added [A], Clean [C], Modified [M] and Missing [!]
+ (as reported by :hg:`status`). The actions are Warn, Remove
+ (from branch) and Delete (from disk):
+
+ ======= == == == ==
+ A C M !
+ ======= == == == ==
+ none W RD W R
+ -f R RD RD R
+ -A W W W R
+ -Af R R R R
+ ======= == == == ==
+
+ Note that remove never deletes files in Added [A] state from the
+ working directory, not even if option --force is specified.
Returns 0 on success, 1 if any warnings encountered.
"""
@@ -3994,8 +4198,8 @@
' to force removal)\n') % m.rel(f))
ret = 1
for f in added:
- ui.warn(_('not removing %s: file has been marked for add (use -f'
- ' to force removal)\n') % m.rel(f))
+ ui.warn(_('not removing %s: file has been marked for add'
+ ' (use forget to undo)\n') % m.rel(f))
ret = 1
for f in sorted(list):
@@ -4051,9 +4255,8 @@
('l', 'list', None, _('list state of files needing merge')),
('m', 'mark', None, _('mark files as resolved')),
('u', 'unmark', None, _('mark files as unresolved')),
- ('t', 'tool', '', _('specify merge tool')),
('n', 'no-status', None, _('hide status prefix'))]
- + walkopts,
+ + mergetoolopts + walkopts,
_('[OPTION]... [FILE]...'))
def resolve(ui, repo, *pats, **opts):
"""redo merges or set/view the merge status of files
@@ -4145,7 +4348,7 @@
[('a', 'all', None, _('revert all changes when no arguments given')),
('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
('r', 'rev', '', _('revert to the specified revision'), _('REV')),
- ('', 'no-backup', None, _('do not save backup copies of files')),
+ ('C', 'no-backup', None, _('do not save backup copies of files')),
] + walkopts + dryrunopts,
_('[OPTION]... [-r REV] [NAME]...'))
def revert(ui, repo, *pats, **opts):
@@ -4653,6 +4856,22 @@
I = ignored
= origin of the previous file listed as A (added)
+ .. container:: verbose
+
+ Examples:
+
+ - show changes in the working directory relative to a changeset:
+
+ hg status --rev 9353
+
+ - show all changes including copies in an existing changeset::
+
+ hg status --copies --change 9353
+
+ - get a NUL separated list of added files, suitable for xargs::
+
+ hg status -an0
+
Returns 0 on success.
"""
@@ -4727,6 +4946,7 @@
ctx = repo[None]
parents = ctx.parents()
pnode = parents[0].node()
+ marks = []
for p in parents:
# label with log.changeset (instead of log.parent) since this
@@ -4735,7 +4955,7 @@
label='log.changeset')
ui.write(' '.join(p.tags()), label='log.tag')
if p.bookmarks():
- ui.write(' ' + ' '.join(p.bookmarks()), label='log.bookmark')
+ marks.extend(p.bookmarks())
if p.rev() == -1:
if not len(repo):
ui.write(_(' (empty repository)'))
@@ -4754,6 +4974,20 @@
else:
ui.status(m, label='log.branch')
+ if marks:
+ current = repo._bookmarkcurrent
+ ui.write(_('bookmarks:'), label='log.bookmark')
+ if current is not None:
+ try:
+ marks.remove(current)
+ ui.write(' *' + current, label='bookmarks.current')
+ except ValueError:
+ # current bookmark not in parent ctx marks
+ pass
+ for m in marks:
+ ui.write(' ' + m, label='log.bookmark')
+ ui.write('\n', label='log.bookmark')
+
st = list(repo.status(unknown=True))[:6]
c = repo.dirstate.copies()
@@ -4988,19 +5222,22 @@
for t, n in reversed(repo.tagslist()):
if ui.quiet:
- ui.write("%s\n" % t)
+ ui.write("%s\n" % t, label='tags.normal')
continue
hn = hexfunc(n)
r = "%5d:%s" % (repo.changelog.rev(n), hn)
+ rev = ui.label(r, 'log.changeset')
spaces = " " * (30 - encoding.colwidth(t))
+ tag = ui.label(t, 'tags.normal')
if ui.verbose:
if repo.tagtype(t) == 'local':
tagtype = " local"
+ tag = ui.label(t, 'tags.local')
else:
tagtype = ""
- ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
+ ui.write("%s%s %s%s\n" % (tag, spaces, rev, tagtype))
@command('tip',
[('p', 'patch', None, _('show patch')),
--- a/mercurial/commandserver.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/commandserver.py Wed Sep 28 16:11:53 2011 -0500
@@ -185,6 +185,7 @@
copiedui = self.ui.copy()
self.repo.baseui = copiedui
self.repo.ui = self.repo.dirstate._ui = self.repoui.copy()
+ self.repo.invalidate()
req = dispatch.request(args[:], copiedui, self.repo, self.cin,
self.cout, self.cerr)
--- a/mercurial/demandimport.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/demandimport.py Wed Sep 28 16:11:53 2011 -0500
@@ -27,6 +27,17 @@
import __builtin__
_origimport = __import__
+nothing = object()
+
+try:
+ _origimport(__builtin__.__name__, {}, {}, None, -1)
+except TypeError: # no level argument
+ def _import(name, globals, locals, fromlist, level):
+ "call _origimport with no level argument"
+ return _origimport(name, globals, locals, fromlist)
+else:
+ _import = _origimport
+
class _demandmod(object):
"""module demand-loader and proxy"""
def __init__(self, name, globals, locals):
@@ -50,7 +61,7 @@
h, t = p, None
if '.' in p:
h, t = p.split('.', 1)
- if not hasattr(mod, h):
+ if getattr(mod, h, nothing) is nothing:
setattr(mod, h, _demandmod(p, mod.__dict__, mod.__dict__))
elif t:
subload(getattr(mod, h), t)
@@ -81,20 +92,14 @@
def _demandimport(name, globals=None, locals=None, fromlist=None, level=-1):
if not locals or name in ignore or fromlist == ('*',):
# these cases we can't really delay
- if level == -1:
- return _origimport(name, globals, locals, fromlist)
- else:
- return _origimport(name, globals, locals, fromlist, level)
+ return _import(name, globals, locals, fromlist, level)
elif not fromlist:
# import a [as b]
if '.' in name: # a.b
base, rest = name.split('.', 1)
# email.__init__ loading email.mime
if globals and globals.get('__name__', None) == base:
- if level != -1:
- return _origimport(name, globals, locals, fromlist, level)
- else:
- return _origimport(name, globals, locals, fromlist)
+ return _import(name, globals, locals, fromlist, level)
# if a is already demand-loaded, add b to its submodule list
if base in locals:
if isinstance(locals[base], _demandmod):
@@ -109,12 +114,12 @@
mod = _origimport(name, globals, locals)
# recurse down the module chain
for comp in name.split('.')[1:]:
- if not hasattr(mod, comp):
+ if getattr(mod, comp, nothing) is nothing:
setattr(mod, comp, _demandmod(comp, mod.__dict__, mod.__dict__))
mod = getattr(mod, comp)
for x in fromlist:
# set requested submodules for demand load
- if not hasattr(mod, x):
+ if getattr(mod, x, nothing) is nothing:
setattr(mod, x, _demandmod(x, mod.__dict__, locals))
return mod
@@ -137,6 +142,8 @@
# raise ImportError if x not defined
'__main__',
'_ssl', # conditional imports in the stdlib, issue1964
+ 'rfc822',
+ 'mimetools',
]
def enable():
@@ -146,4 +153,3 @@
def disable():
"disable global demand-loading of modules"
__builtin__.__import__ = _origimport
-
--- a/mercurial/dirstate.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/dirstate.py Wed Sep 28 16:11:53 2011 -0500
@@ -453,7 +453,7 @@
write(e)
write(f)
st.write(cs.getvalue())
- st.rename()
+ st.close()
self._lastnormaltime = None
self._dirty = self._dirtypl = False
--- a/mercurial/dispatch.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/dispatch.py Wed Sep 28 16:11:53 2011 -0500
@@ -123,6 +123,9 @@
else:
ui.warn(_("hg: %s\n") % inst.args[1])
commands.help_(ui, 'shortlist')
+ except error.OutOfBandError, inst:
+ ui.warn("abort: remote error:\n")
+ ui.warn(''.join(inst.args))
except error.RepoError, inst:
ui.warn(_("abort: %s!\n") % inst)
if inst.hint:
@@ -159,16 +162,16 @@
elif m in "zlib".split():
ui.warn(_("(is your Python install correct?)\n"))
except IOError, inst:
- if hasattr(inst, "code"):
+ if util.safehasattr(inst, "code"):
ui.warn(_("abort: %s\n") % inst)
- elif hasattr(inst, "reason"):
+ elif util.safehasattr(inst, "reason"):
try: # usually it is in the form (errno, strerror)
reason = inst.reason.args[1]
except (AttributeError, IndexError):
# it might be anything, for example a string
reason = inst.reason
ui.warn(_("abort: error: %s\n") % reason)
- elif hasattr(inst, "args") and inst.args[0] == errno.EPIPE:
+ elif util.safehasattr(inst, "args") and inst.args[0] == errno.EPIPE:
if ui.debugflag:
ui.warn(_("broken pipe\n"))
elif getattr(inst, "strerror", None):
@@ -338,7 +341,7 @@
ui.debug("alias '%s' shadows command '%s'\n" %
(self.name, self.cmdname))
- if hasattr(self, 'shell'):
+ if util.safehasattr(self, 'shell'):
return self.fn(ui, *args, **opts)
else:
try:
@@ -483,15 +486,14 @@
lui = ui.copy()
lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
- if rpath:
+ if rpath and rpath[-1]:
path = lui.expandpath(rpath[-1])
lui = ui.copy()
lui.readconfig(os.path.join(path, ".hg", "hgrc"), path)
return path, lui
-def _checkshellalias(ui, args):
- cwd = os.getcwd()
+def _checkshellalias(lui, ui, args):
norepo = commands.norepo
options = {}
@@ -503,12 +505,6 @@
if not args:
return
- _parseconfig(ui, options['config'])
- if options['cwd']:
- os.chdir(options['cwd'])
-
- path, lui = _getlocal(ui, [options['repository']])
-
cmdtable = commands.table.copy()
addaliases(lui, cmdtable)
@@ -517,28 +513,22 @@
aliases, entry = cmdutil.findcmd(cmd, cmdtable, lui.config("ui", "strict"))
except (error.AmbiguousCommand, error.UnknownCommand):
commands.norepo = norepo
- os.chdir(cwd)
return
cmd = aliases[0]
fn = entry[0]
- if cmd and hasattr(fn, 'shell'):
+ if cmd and util.safehasattr(fn, 'shell'):
d = lambda: fn(ui, *args[1:])
return lambda: runcommand(lui, None, cmd, args[:1], ui, options, d, [], {})
commands.norepo = norepo
- os.chdir(cwd)
_loaded = set()
def _dispatch(req):
args = req.args
ui = req.ui
- shellaliasfn = _checkshellalias(ui, args)
- if shellaliasfn:
- return shellaliasfn()
-
# read --config before doing anything else
# (e.g. to change trust settings for reading .hg/hgrc)
cfgs = _parseconfig(ui, _earlygetopt(['--config'], args))
@@ -551,6 +541,12 @@
rpath = _earlygetopt(["-R", "--repository", "--repo"], args)
path, lui = _getlocal(ui, rpath)
+ # Now that we're operating in the right directory/repository with
+ # the right config settings, check for shell aliases
+ shellaliasfn = _checkshellalias(lui, ui, args)
+ if shellaliasfn:
+ return shellaliasfn()
+
# Configure extensions in phases: uisetup, extsetup, cmdtable, and
# reposetup. Programs like TortoiseHg will call _dispatch several
# times so we keep track of configured extensions in _loaded.
@@ -635,10 +631,10 @@
for ui_ in uis:
ui_.setconfig('web', 'cacerts', '')
+ if options['version']:
+ return commands.version_(ui)
if options['help']:
- return commands.help_(ui, cmd, options['version'])
- elif options['version']:
- return commands.version_(ui)
+ return commands.help_(ui, cmd)
elif not cmd:
return commands.help_(ui, 'shortlist')
--- a/mercurial/encoding.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/encoding.py Wed Sep 28 16:11:53 2011 -0500
@@ -139,7 +139,7 @@
and "WFA" or "WF")
def colwidth(s):
- "Find the column width of a UTF-8 string for display"
+ "Find the column width of a string for display in the local encoding"
return ucolwidth(s.decode(encoding, 'replace'))
def ucolwidth(d):
@@ -149,6 +149,14 @@
return sum([eaw(c) in wide and 2 or 1 for c in d])
return len(d)
+def getcols(s, start, c):
+ '''Use colwidth to find a c-column substring of s starting at byte
+ index start'''
+ for x in xrange(start + c, len(s)):
+ t = s[start:x]
+ if colwidth(t) == c:
+ return t
+
def lower(s):
"best-effort encoding-aware case-folding of local string s"
try:
--- a/mercurial/error.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/error.py Wed Sep 28 16:11:53 2011 -0500
@@ -39,6 +39,9 @@
class ConfigError(Abort):
'Exception raised when parsing config files'
+class OutOfBandError(Exception):
+ 'Exception raised when a remote repo reports failure'
+
class ParseError(Exception):
'Exception raised when parsing config files (msg[, pos])'
--- a/mercurial/extensions.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/extensions.py Wed Sep 28 16:11:53 2011 -0500
@@ -124,7 +124,7 @@
where orig is the original (wrapped) function, and *args, **kwargs
are the arguments passed to it.
'''
- assert hasattr(wrapper, '__call__')
+ assert util.safehasattr(wrapper, '__call__')
aliases, entry = cmdutil.findcmd(command, table)
for alias, e in table.iteritems():
if e is entry:
@@ -177,12 +177,12 @@
your end users, you should play nicely with others by using the
subclass trick.
'''
- assert hasattr(wrapper, '__call__')
+ assert util.safehasattr(wrapper, '__call__')
def wrap(*args, **kwargs):
return wrapper(origfn, *args, **kwargs)
origfn = getattr(container, funcname)
- assert hasattr(origfn, '__call__')
+ assert util.safehasattr(origfn, '__call__')
setattr(container, funcname, wrap)
return origfn
--- a/mercurial/fancyopts.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/fancyopts.py Wed Sep 28 16:11:53 2011 -0500
@@ -75,7 +75,7 @@
# copy defaults to state
if isinstance(default, list):
state[name] = default[:]
- elif hasattr(default, '__call__'):
+ elif getattr(default, '__call__', False):
state[name] = None
else:
state[name] = default
--- a/mercurial/hbisect.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hbisect.py Wed Sep 28 16:11:53 2011 -0500
@@ -8,7 +8,7 @@
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
-import os
+import os, error
from i18n import _
from node import short, hex
import util
@@ -35,17 +35,18 @@
# build visit array
ancestors = [None] * (len(changelog) + 1) # an extra for [-1]
- # set nodes descended from goodrev
- ancestors[goodrev] = []
+ # set nodes descended from goodrevs
+ for rev in goodrevs:
+ ancestors[rev] = []
for rev in xrange(goodrev + 1, len(changelog)):
for prev in clparents(rev):
if ancestors[prev] == []:
ancestors[rev] = []
# clear good revs from array
- for node in goodrevs:
- ancestors[node] = None
- for rev in xrange(len(changelog), -1, -1):
+ for rev in goodrevs:
+ ancestors[rev] = None
+ for rev in xrange(len(changelog), goodrev, -1):
if ancestors[rev] is None:
for prev in clparents(rev):
ancestors[prev] = None
@@ -149,7 +150,102 @@
for kind in state:
for node in state[kind]:
f.write("%s %s\n" % (kind, hex(node)))
- f.rename()
+ f.close()
finally:
wlock.release()
+def get(repo, status):
+ """
+ Return a list of revision(s) that match the given status:
+
+ - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
+ - ``goods``, ``bads`` : csets topologicaly good/bad
+ - ``range`` : csets taking part in the bisection
+ - ``pruned`` : csets that are goods, bads or skipped
+ - ``untested`` : csets whose fate is yet unknown
+ - ``ignored`` : csets ignored due to DAG topology
+ """
+ state = load_state(repo)
+ if status in ('good', 'bad', 'skip'):
+ return [repo.changelog.rev(n) for n in state[status]]
+ else:
+ # In the floowing sets, we do *not* call 'bisect()' with more
+ # than one level of recusrsion, because that can be very, very
+ # time consuming. Instead, we always develop the expression as
+ # much as possible.
+
+ # 'range' is all csets that make the bisection:
+ # - have a good ancestor and a bad descendant, or conversely
+ # that's because the bisection can go either way
+ range = '( bisect(bad)::bisect(good) | bisect(good)::bisect(bad) )'
+
+ _t = [c.rev() for c in repo.set('bisect(good)::bisect(bad)')]
+ # The sets of topologically good or bad csets
+ if len(_t) == 0:
+ # Goods are topologically after bads
+ goods = 'bisect(good)::' # Pruned good csets
+ bads = '::bisect(bad)' # Pruned bad csets
+ else:
+ # Goods are topologically before bads
+ goods = '::bisect(good)' # Pruned good csets
+ bads = 'bisect(bad)::' # Pruned bad csets
+
+ # 'pruned' is all csets whose fate is already known: good, bad, skip
+ skips = 'bisect(skip)' # Pruned skipped csets
+ pruned = '( (%s) | (%s) | (%s) )' % (goods, bads, skips)
+
+ # 'untested' is all cset that are- in 'range', but not in 'pruned'
+ untested = '( (%s) - (%s) )' % (range, pruned)
+
+ # 'ignored' is all csets that were not used during the bisection
+ # due to DAG topology, but may however have had an impact.
+ # Eg., a branch merged between bads and goods, but whose branch-
+ # point is out-side of the range.
+ iba = '::bisect(bad) - ::bisect(good)' # Ignored bads' ancestors
+ iga = '::bisect(good) - ::bisect(bad)' # Ignored goods' ancestors
+ ignored = '( ( (%s) | (%s) ) - (%s) )' % (iba, iga, range)
+
+ if status == 'range':
+ return [c.rev() for c in repo.set(range)]
+ elif status == 'pruned':
+ return [c.rev() for c in repo.set(pruned)]
+ elif status == 'untested':
+ return [c.rev() for c in repo.set(untested)]
+ elif status == 'ignored':
+ return [c.rev() for c in repo.set(ignored)]
+ elif status == "goods":
+ return [c.rev() for c in repo.set(goods)]
+ elif status == "bads":
+ return [c.rev() for c in repo.set(bads)]
+
+ else:
+ raise error.ParseError(_('invalid bisect state'))
+
+def label(repo, node, short=False):
+ rev = repo.changelog.rev(node)
+
+ # Try explicit sets
+ if rev in get(repo, 'good'):
+ return _('good')
+ if rev in get(repo, 'bad'):
+ return _('bad')
+ if rev in get(repo, 'skip'):
+ return _('skipped')
+ if rev in get(repo, 'untested'):
+ return _('untested')
+ if rev in get(repo, 'ignored'):
+ return _('ignored')
+
+ # Try implicit sets
+ if rev in get(repo, 'goods'):
+ return _('good (implicit)')
+ if rev in get(repo, 'bads'):
+ return _('bad (implicit)')
+
+ return None
+
+def shortlabel(label):
+ if label:
+ return label[0].upper()
+
+ return None
--- a/mercurial/help.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/help.py Wed Sep 28 16:11:53 2011 -0500
@@ -31,7 +31,7 @@
"""Return a delayed loader for help/topic.txt."""
def loader():
- if hasattr(sys, 'frozen'):
+ if util.mainfrozen():
module = sys.executable
else:
module = __file__
--- a/mercurial/help/config.txt Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/help/config.txt Wed Sep 28 16:11:53 2011 -0500
@@ -223,6 +223,10 @@
``$HG_ARGS`` expand to the arguments given to Mercurial. In the ``hg
echo foo`` call above, ``$HG_ARGS`` would expand to ``echo foo``.
+.. note:: Some global configuration options such as ``-R`` are
+ processed before shell aliases and will thus not be passed to
+ aliases.
+
``auth``
""""""""
@@ -1261,6 +1265,12 @@
``ipv6``
Whether to use IPv6. Default is False.
+``logoimg``
+ File name of the logo image that some templates display on each page.
+ The file name is relative to ``staticurl``. That is, the full path to
+ the logo image is "staticurl/logoimg".
+ If unset, ``hglogo.png`` will be used.
+
``logourl``
Base URL to use for logos. If unset, ``http://mercurial.selenic.com/``
will be used.
--- a/mercurial/hg.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hg.py Wed Sep 28 16:11:53 2011 -0500
@@ -98,9 +98,9 @@
hook(ui, repo)
return repo
-def peer(ui, opts, path, create=False):
+def peer(uiorrepo, opts, path, create=False):
'''return a repository peer for the specified path'''
- rui = remoteui(ui, opts)
+ rui = remoteui(uiorrepo, opts)
return repository(rui, path, create)
def defaultdest(source):
@@ -174,6 +174,36 @@
continue
_update(r, uprev)
+def copystore(ui, srcrepo, destpath):
+ '''copy files from store of srcrepo in destpath
+
+ returns destlock
+ '''
+ destlock = None
+ try:
+ hardlink = None
+ num = 0
+ for f in srcrepo.store.copylist():
+ src = os.path.join(srcrepo.sharedpath, f)
+ dst = os.path.join(destpath, f)
+ dstbase = os.path.dirname(dst)
+ if dstbase and not os.path.exists(dstbase):
+ os.mkdir(dstbase)
+ if os.path.exists(src):
+ if dst.endswith('data'):
+ # lock to avoid premature writing to the target
+ destlock = lock.lock(os.path.join(dstbase, "lock"))
+ hardlink, n = util.copyfiles(src, dst, hardlink)
+ num += n
+ if hardlink:
+ ui.debug("linked %d files\n" % num)
+ else:
+ ui.debug("copied %d files\n" % num)
+ return destlock
+ except:
+ release(destlock)
+ raise
+
def clone(ui, peeropts, source, dest=None, pull=False, rev=None,
update=True, stream=False, branch=None):
"""Make a copy of an existing repository.
@@ -287,24 +317,7 @@
% dest)
raise
- hardlink = None
- num = 0
- for f in srcrepo.store.copylist():
- src = os.path.join(srcrepo.sharedpath, f)
- dst = os.path.join(destpath, f)
- dstbase = os.path.dirname(dst)
- if dstbase and not os.path.exists(dstbase):
- os.mkdir(dstbase)
- if os.path.exists(src):
- if dst.endswith('data'):
- # lock to avoid premature writing to the target
- destlock = lock.lock(os.path.join(dstbase, "lock"))
- hardlink, n = util.copyfiles(src, dst, hardlink)
- num += n
- if hardlink:
- ui.debug("linked %d files\n" % num)
- else:
- ui.debug("copied %d files\n" % num)
+ destlock = copystore(ui, srcrepo, destpath)
# we need to re-init the repo after manually copying the data
# into it
@@ -537,7 +550,7 @@
def remoteui(src, opts):
'build a remote ui from ui or repo and opts'
- if hasattr(src, 'baseui'): # looks like a repository
+ if util.safehasattr(src, 'baseui'): # looks like a repository
dst = src.baseui.copy() # drop repo-specific config
src = src.ui # copy target options from repo
else: # assume it's a global ui object
--- a/mercurial/hgweb/hgweb_mod.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hgweb/hgweb_mod.py Wed Sep 28 16:11:53 2011 -0500
@@ -7,7 +7,7 @@
# GNU General Public License version 2 or any later version.
import os
-from mercurial import ui, hg, hook, error, encoding, templater
+from mercurial import ui, hg, hook, error, encoding, templater, util
from common import get_stat, ErrorResponse, permhooks, caching
from common import HTTP_OK, HTTP_NOT_MODIFIED, HTTP_BAD_REQUEST
from common import HTTP_NOT_FOUND, HTTP_SERVER_ERROR
@@ -148,7 +148,7 @@
cmd = cmd[style + 1:]
# avoid accepting e.g. style parameter as command
- if hasattr(webcommands, cmd):
+ if util.safehasattr(webcommands, cmd):
req.form['cmd'] = [cmd]
else:
cmd = ''
@@ -236,6 +236,7 @@
port = port != default_port and (":" + port) or ""
urlbase = '%s://%s%s' % (proto, req.env['SERVER_NAME'], port)
logourl = self.config("web", "logourl", "http://mercurial.selenic.com/")
+ logoimg = self.config("web", "logoimg", "hglogo.png")
staticurl = self.config("web", "staticurl") or req.url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
@@ -276,6 +277,7 @@
tmpl = templater.templater(mapfile,
defaults={"url": req.url,
"logourl": logourl,
+ "logoimg": logoimg,
"staticurl": staticurl,
"urlbase": urlbase,
"repo": self.reponame,
--- a/mercurial/hgweb/hgwebdir_mod.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hgweb/hgwebdir_mod.py Wed Sep 28 16:11:53 2011 -0500
@@ -51,6 +51,33 @@
yield (prefix + '/' +
util.pconvert(path[len(roothead):]).lstrip('/')).strip('/'), path
+def geturlcgivars(baseurl, port):
+ """
+ Extract CGI variables from baseurl
+
+ >>> geturlcgivars("http://host.org/base", "80")
+ ('host.org', '80', '/base')
+ >>> geturlcgivars("http://host.org:8000/base", "80")
+ ('host.org', '8000', '/base')
+ >>> geturlcgivars('/base', 8000)
+ ('', '8000', '/base')
+ >>> geturlcgivars("base", '8000')
+ ('', '8000', '/base')
+ >>> geturlcgivars("http://host", '8000')
+ ('host', '8000', '/')
+ >>> geturlcgivars("http://host/", '8000')
+ ('host', '8000', '/')
+ """
+ u = util.url(baseurl)
+ name = u.host or ''
+ if u.port:
+ port = u.port
+ path = u.path or ""
+ if not path.startswith('/'):
+ path = '/' + path
+
+ return name, str(port), path
+
class hgwebdir(object):
refreshinterval = 20
@@ -348,6 +375,7 @@
start = url[-1] == '?' and '&' or '?'
sessionvars = webutil.sessionvars(vars, start)
logourl = config('web', 'logourl', 'http://mercurial.selenic.com/')
+ logoimg = config('web', 'logoimg', 'hglogo.png')
staticurl = config('web', 'staticurl') or url + 'static/'
if not staticurl.endswith('/'):
staticurl += '/'
@@ -358,17 +386,14 @@
"motd": motd,
"url": url,
"logourl": logourl,
+ "logoimg": logoimg,
"staticurl": staticurl,
"sessionvars": sessionvars})
return tmpl
def updatereqenv(self, env):
if self._baseurl is not None:
- u = util.url(self._baseurl)
- env['SERVER_NAME'] = u.host
- if u.port:
- env['SERVER_PORT'] = u.port
- path = u.path or ""
- if not path.startswith('/'):
- path = '/' + path
+ name, port, path = geturlcgivars(self._baseurl, env['SERVER_PORT'])
+ env['SERVER_NAME'] = name
+ env['SERVER_PORT'] = port
env['SCRIPT_NAME'] = path
--- a/mercurial/hgweb/protocol.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hgweb/protocol.py Wed Sep 28 16:11:53 2011 -0500
@@ -10,6 +10,7 @@
from common import HTTP_OK
HGTYPE = 'application/mercurial-0.1'
+HGERRTYPE = 'application/hg-error'
class webproto(object):
def __init__(self, req, ui):
@@ -90,3 +91,7 @@
rsp = '0\n%s\n' % rsp.res
req.respond(HTTP_OK, HGTYPE, length=len(rsp))
return [rsp]
+ elif isinstance(rsp, wireproto.ooberror):
+ rsp = rsp.message
+ req.respond(HTTP_OK, HGERRTYPE, length=len(rsp))
+ return [rsp]
--- a/mercurial/hgweb/request.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hgweb/request.py Wed Sep 28 16:11:53 2011 -0500
@@ -101,7 +101,7 @@
self.headers = []
def write(self, thing):
- if hasattr(thing, "__iter__"):
+ if util.safehasattr(thing, "__iter__"):
for part in thing:
self.write(part)
else:
--- a/mercurial/hgweb/server.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hgweb/server.py Wed Sep 28 16:11:53 2011 -0500
@@ -248,7 +248,7 @@
from threading import activeCount
_mixin = SocketServer.ThreadingMixIn
except ImportError:
- if hasattr(os, "fork"):
+ if util.safehasattr(os, "fork"):
_mixin = SocketServer.ForkingMixIn
else:
class _mixin(object):
--- a/mercurial/hgweb/webutil.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hgweb/webutil.py Wed Sep 28 16:11:53 2011 -0500
@@ -72,7 +72,7 @@
d['date'] = s.date()
d['description'] = s.description()
d['branch'] = s.branch()
- if hasattr(s, 'path'):
+ if util.safehasattr(s, 'path'):
d['file'] = s.path()
yield d
--- a/mercurial/hgweb/wsgicgi.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hgweb/wsgicgi.py Wed Sep 28 16:11:53 2011 -0500
@@ -78,5 +78,4 @@
for chunk in content:
write(chunk)
finally:
- if hasattr(content, 'close'):
- content.close()
+ getattr(content, 'close', lambda : None)()
--- a/mercurial/hook.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/hook.py Wed Sep 28 16:11:53 2011 -0500
@@ -21,14 +21,14 @@
ui.note(_("calling hook %s: %s\n") % (hname, funcname))
obj = funcname
- if not hasattr(obj, '__call__'):
+ if not util.safehasattr(obj, '__call__'):
d = funcname.rfind('.')
if d == -1:
raise util.Abort(_('%s hook is invalid ("%s" not in '
'a module)') % (hname, funcname))
modname = funcname[:d]
oldpaths = sys.path
- if hasattr(sys, "frozen"):
+ if util.mainfrozen():
# binary installs require sys.path manipulation
modpath, modfile = os.path.split(modname)
if modpath and modfile:
@@ -60,7 +60,7 @@
raise util.Abort(_('%s hook is invalid '
'("%s" is not defined)') %
(hname, funcname))
- if not hasattr(obj, '__call__'):
+ if not util.safehasattr(obj, '__call__'):
raise util.Abort(_('%s hook is invalid '
'("%s" is not callable)') %
(hname, funcname))
@@ -99,7 +99,7 @@
env = {}
for k, v in args.iteritems():
- if hasattr(v, '__call__'):
+ if util.safehasattr(v, '__call__'):
v = v()
if isinstance(v, dict):
# make the dictionary element order stable across Python
@@ -149,7 +149,7 @@
for hname, cmd in ui.configitems('hooks'):
if hname.split('.')[0] != name or not cmd:
continue
- if hasattr(cmd, '__call__'):
+ if util.safehasattr(cmd, '__call__'):
r = _pythonhook(ui, repo, name, hname, cmd, args, throw) or r
elif cmd.startswith('python:'):
if cmd.count(':') >= 2:
--- a/mercurial/httprepo.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/httprepo.py Wed Sep 28 16:11:53 2011 -0500
@@ -44,8 +44,7 @@
def __del__(self):
for h in self.urlopener.handlers:
h.close()
- if hasattr(h, "close_all"):
- h.close_all()
+ getattr(h, "close_all", lambda : None)()
def url(self):
return self.path
@@ -139,6 +138,8 @@
proto = resp.headers.get('content-type', '')
safeurl = util.hidepassword(self._url)
+ if proto.startswith('application/hg-error'):
+ raise error.OutOfBandError(resp.read())
# accept old "text/plain" and "application/hg-changegroup" for now
if not (proto.startswith('application/mercurial-') or
proto.startswith('text/plain') or
--- a/mercurial/i18n.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/i18n.py Wed Sep 28 16:11:53 2011 -0500
@@ -9,7 +9,7 @@
import gettext, sys, os
# modelled after templater.templatepath:
-if hasattr(sys, 'frozen'):
+if getattr(sys, 'frozen', None) is not None:
module = sys.executable
else:
module = __file__
@@ -61,4 +61,3 @@
_ = lambda message: message
else:
_ = gettext
-
--- a/mercurial/keepalive.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/keepalive.py Wed Sep 28 16:11:53 2011 -0500
@@ -547,13 +547,14 @@
print "send:", repr(str)
try:
blocksize = 8192
- if hasattr(str,'read') :
+ read = getattr(str, 'read', None)
+ if read is not None:
if self.debuglevel > 0:
print "sendIng a read()able"
- data = str.read(blocksize)
+ data = read(blocksize)
while data:
self.sock.sendall(data)
- data = str.read(blocksize)
+ data = read(blocksize)
else:
self.sock.sendall(str)
except socket.error, v:
--- a/mercurial/localrepo.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/localrepo.py Wed Sep 28 16:11:53 2011 -0500
@@ -10,13 +10,14 @@
import repo, changegroup, subrepo, discovery, pushkey
import changelog, dirstate, filelog, manifest, context, bookmarks
import lock, transaction, store, encoding
-import scmutil, util, extensions, hook, error
+import scmutil, util, extensions, hook, error, revset
import match as matchmod
import merge as mergemod
import tags as tagsmod
from lock import release
import weakref, errno, os, time, inspect
propertycache = util.propertycache
+filecache = scmutil.filecache
class localrepository(repo.repository):
capabilities = set(('lookup', 'changegroupsubset', 'branchmap', 'pushkey',
@@ -63,6 +64,7 @@
)
if self.ui.configbool('format', 'generaldelta', False):
requirements.append("generaldelta")
+ requirements = set(requirements)
else:
raise error.RepoError(_("repository %s not found") % path)
elif create:
@@ -77,7 +79,7 @@
self.sharedpath = self.path
try:
- s = os.path.realpath(self.opener.read("sharedpath"))
+ s = os.path.realpath(self.opener.read("sharedpath").rstrip('\n'))
if not os.path.exists(s):
raise error.RepoError(
_('.hg/sharedpath points to nonexistent directory %s') % s)
@@ -95,21 +97,19 @@
if create:
self._writerequirements()
- # These two define the set of tags for this repository. _tags
- # maps tag name to node; _tagtypes maps tag name to 'global' or
- # 'local'. (Global tags are defined by .hgtags across all
- # heads, and local tags are defined in .hg/localtags.) They
- # constitute the in-memory cache of tags.
- self._tags = None
- self._tagtypes = None
self._branchcache = None
self._branchcachetip = None
- self.nodetagscache = None
self.filterpats = {}
self._datafilters = {}
self._transref = self._lockref = self._wlockref = None
+ # A cache for various files under .hg/ that tracks file changes,
+ # (used by the filecache decorator)
+ #
+ # Maps a property name to its util.filecacheentry
+ self._filecache = {}
+
def _applyrequirements(self, requirements):
self.requirements = requirements
openerreqs = set(('revlogv1', 'generaldelta'))
@@ -159,15 +159,15 @@
parts.pop()
return False
- @util.propertycache
+ @filecache('bookmarks')
def _bookmarks(self):
return bookmarks.read(self)
- @util.propertycache
+ @filecache('bookmarks.current')
def _bookmarkcurrent(self):
return bookmarks.readcurrent(self)
- @propertycache
+ @filecache('00changelog.i', True)
def changelog(self):
c = changelog.changelog(self.sopener)
if 'HG_PENDING' in os.environ:
@@ -176,11 +176,11 @@
c.readpending('00changelog.i.a')
return c
- @propertycache
+ @filecache('00manifest.i', True)
def manifest(self):
return manifest.manifest(self.sopener)
- @propertycache
+ @filecache('dirstate')
def dirstate(self):
warned = [0]
def validate(node):
@@ -217,6 +217,17 @@
for i in xrange(len(self)):
yield i
+ def set(self, expr, *args):
+ '''
+ Yield a context for each matching revision, after doing arg
+ replacement via revset.formatspec
+ '''
+
+ expr = revset.formatspec(expr, *args)
+ m = revset.match(None, expr)
+ for r in m(self, range(len(self))):
+ yield self[r]
+
def url(self):
return 'file:' + self.root
@@ -249,8 +260,8 @@
fp.write('\n')
for name in names:
m = munge and munge(name) or name
- if self._tagtypes and name in self._tagtypes:
- old = self._tags.get(name, nullid)
+ if self._tagscache.tagtypes and name in self._tagscache.tagtypes:
+ old = self.tags().get(name, nullid)
fp.write('%s %s\n' % (hex(old), m))
fp.write('%s %s\n' % (hex(node), m))
fp.close()
@@ -325,12 +336,31 @@
self.tags() # instantiate the cache
self._tag(names, node, message, local, user, date)
+ @propertycache
+ def _tagscache(self):
+ '''Returns a tagscache object that contains various tags related caches.'''
+
+ # This simplifies its cache management by having one decorated
+ # function (this one) and the rest simply fetch things from it.
+ class tagscache(object):
+ def __init__(self):
+ # These two define the set of tags for this repository. tags
+ # maps tag name to node; tagtypes maps tag name to 'global' or
+ # 'local'. (Global tags are defined by .hgtags across all
+ # heads, and local tags are defined in .hg/localtags.)
+ # They constitute the in-memory cache of tags.
+ self.tags = self.tagtypes = None
+
+ self.nodetagscache = self.tagslist = None
+
+ cache = tagscache()
+ cache.tags, cache.tagtypes = self._findtags()
+
+ return cache
+
def tags(self):
'''return a mapping of tag to node'''
- if self._tags is None:
- (self._tags, self._tagtypes) = self._findtags()
-
- return self._tags
+ return self._tagscache.tags
def _findtags(self):
'''Do the hard work of finding tags. Return a pair of dicts
@@ -379,27 +409,29 @@
None : tag does not exist
'''
- self.tags()
-
- return self._tagtypes.get(tagname)
+ return self._tagscache.tagtypes.get(tagname)
def tagslist(self):
'''return a list of tags ordered by revision'''
- l = []
- for t, n in self.tags().iteritems():
- r = self.changelog.rev(n)
- l.append((r, t, n))
- return [(t, n) for r, t, n in sorted(l)]
+ if not self._tagscache.tagslist:
+ l = []
+ for t, n in self.tags().iteritems():
+ r = self.changelog.rev(n)
+ l.append((r, t, n))
+ self._tagscache.tagslist = [(t, n) for r, t, n in sorted(l)]
+
+ return self._tagscache.tagslist
def nodetags(self, node):
'''return the tags associated with a node'''
- if not self.nodetagscache:
- self.nodetagscache = {}
+ if not self._tagscache.nodetagscache:
+ nodetagscache = {}
for t, n in self.tags().iteritems():
- self.nodetagscache.setdefault(n, []).append(t)
- for tags in self.nodetagscache.itervalues():
+ nodetagscache.setdefault(n, []).append(t)
+ for tags in nodetagscache.itervalues():
tags.sort()
- return self.nodetagscache.get(node, [])
+ self._tagscache.nodetagscache = nodetagscache
+ return self._tagscache.nodetagscache.get(node, [])
def nodebookmarks(self, node):
marks = []
@@ -489,7 +521,7 @@
for label, nodes in branches.iteritems():
for node in nodes:
f.write("%s %s\n" % (hex(node), encoding.fromlocal(label)))
- f.rename()
+ f.close()
except (IOError, OSError):
pass
@@ -728,61 +760,99 @@
wlock = self.wlock()
lock = self.lock()
if os.path.exists(self.sjoin("undo")):
- try:
- args = self.opener.read("undo.desc").splitlines()
- if len(args) >= 3 and self.ui.verbose:
- desc = _("repository tip rolled back to revision %s"
- " (undo %s: %s)\n") % (
- int(args[0]) - 1, args[1], args[2])
- elif len(args) >= 2:
- desc = _("repository tip rolled back to revision %s"
- " (undo %s)\n") % (
- int(args[0]) - 1, args[1])
- except IOError:
- desc = _("rolling back unknown transaction\n")
- self.ui.status(desc)
- if dryrun:
- return
- transaction.rollback(self.sopener, self.sjoin("undo"),
- self.ui.warn)
- util.rename(self.join("undo.dirstate"), self.join("dirstate"))
- if os.path.exists(self.join('undo.bookmarks')):
- util.rename(self.join('undo.bookmarks'),
- self.join('bookmarks'))
- try:
- branch = self.opener.read("undo.branch")
- self.dirstate.setbranch(branch)
- except IOError:
- self.ui.warn(_("named branch could not be reset, "
- "current branch is still: %s\n")
- % self.dirstate.branch())
- self.invalidate()
- self.dirstate.invalidate()
- self.destroyed()
- parents = tuple([p.rev() for p in self.parents()])
- if len(parents) > 1:
- self.ui.status(_("working directory now based on "
- "revisions %d and %d\n") % parents)
- else:
- self.ui.status(_("working directory now based on "
- "revision %d\n") % parents)
+ return self._rollback(dryrun)
else:
self.ui.warn(_("no rollback information available\n"))
return 1
finally:
release(lock, wlock)
+ def _rollback(self, dryrun):
+ ui = self.ui
+ try:
+ args = self.opener.read('undo.desc').splitlines()
+ (oldlen, desc, detail) = (int(args[0]), args[1], None)
+ if len(args) >= 3:
+ detail = args[2]
+ oldtip = oldlen - 1
+
+ if detail and ui.verbose:
+ msg = (_('repository tip rolled back to revision %s'
+ ' (undo %s: %s)\n')
+ % (oldtip, desc, detail))
+ else:
+ msg = (_('repository tip rolled back to revision %s'
+ ' (undo %s)\n')
+ % (oldtip, desc))
+ except IOError:
+ msg = _('rolling back unknown transaction\n')
+ ui.status(msg)
+ if dryrun:
+ return 0
+
+ parents = self.dirstate.parents()
+ transaction.rollback(self.sopener, self.sjoin('undo'), ui.warn)
+ if os.path.exists(self.join('undo.bookmarks')):
+ util.rename(self.join('undo.bookmarks'),
+ self.join('bookmarks'))
+ self.invalidate()
+
+ parentgone = (parents[0] not in self.changelog.nodemap or
+ parents[1] not in self.changelog.nodemap)
+ if parentgone:
+ util.rename(self.join('undo.dirstate'), self.join('dirstate'))
+ try:
+ branch = self.opener.read('undo.branch')
+ self.dirstate.setbranch(branch)
+ except IOError:
+ ui.warn(_('named branch could not be reset: '
+ 'current branch is still \'%s\'\n')
+ % self.dirstate.branch())
+
+ self.dirstate.invalidate()
+ self.destroyed()
+ parents = tuple([p.rev() for p in self.parents()])
+ if len(parents) > 1:
+ ui.status(_('working directory now based on '
+ 'revisions %d and %d\n') % parents)
+ else:
+ ui.status(_('working directory now based on '
+ 'revision %d\n') % parents)
+ return 0
+
def invalidatecaches(self):
- self._tags = None
- self._tagtypes = None
- self.nodetagscache = None
+ try:
+ delattr(self, '_tagscache')
+ except AttributeError:
+ pass
+
self._branchcache = None # in UTF-8
self._branchcachetip = None
+ def invalidatedirstate(self):
+ '''Invalidates the dirstate, causing the next call to dirstate
+ to check if it was modified since the last time it was read,
+ rereading it if it has.
+
+ This is different to dirstate.invalidate() that it doesn't always
+ rereads the dirstate. Use dirstate.invalidate() if you want to
+ explicitly read the dirstate again (i.e. restoring it to a previous
+ known good state).'''
+ try:
+ delattr(self, 'dirstate')
+ except AttributeError:
+ pass
+
def invalidate(self):
- for a in ("changelog", "manifest", "_bookmarks", "_bookmarkcurrent"):
- if a in self.__dict__:
- delattr(self, a)
+ for k in self._filecache:
+ # dirstate is invalidated separately in invalidatedirstate()
+ if k == 'dirstate':
+ continue
+
+ try:
+ delattr(self, k)
+ except AttributeError:
+ pass
self.invalidatecaches()
def _lock(self, lockname, wait, releasefn, acquirefn, desc):
@@ -809,7 +879,14 @@
l.lock()
return l
- l = self._lock(self.sjoin("lock"), wait, self.store.write,
+ def unlock():
+ self.store.write()
+ for k, ce in self._filecache.items():
+ if k == 'dirstate':
+ continue
+ ce.refresh()
+
+ l = self._lock(self.sjoin("lock"), wait, unlock,
self.invalidate, _('repository %s') % self.origroot)
self._lockref = weakref.ref(l)
return l
@@ -823,8 +900,14 @@
l.lock()
return l
- l = self._lock(self.join("wlock"), wait, self.dirstate.write,
- self.dirstate.invalidate, _('working directory of %s') %
+ def unlock():
+ self.dirstate.write()
+ ce = self._filecache.get('dirstate')
+ if ce:
+ ce.refresh()
+
+ l = self._lock(self.join("wlock"), wait, unlock,
+ self.invalidatedirstate, _('working directory of %s') %
self.origroot)
self._wlockref = weakref.ref(l)
return l
--- a/mercurial/lsprof.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/lsprof.py Wed Sep 28 16:11:53 2011 -0500
@@ -86,9 +86,7 @@
for k, v in list(sys.modules.iteritems()):
if v is None:
continue
- if not hasattr(v, '__file__'):
- continue
- if not isinstance(v.__file__, str):
+ if not isinstance(getattr(v, '__file__', None), str):
continue
if v.__file__.startswith(code.co_filename):
mname = _fn2mod[code.co_filename] = k
--- a/mercurial/mail.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/mail.py Wed Sep 28 16:11:53 2011 -0500
@@ -37,7 +37,7 @@
# backward compatible: when tls = true, we use starttls.
starttls = tls == 'starttls' or util.parsebool(tls)
smtps = tls == 'smtps'
- if (starttls or smtps) and not hasattr(socket, 'ssl'):
+ if (starttls or smtps) and not util.safehasattr(socket, 'ssl'):
raise util.Abort(_("can't use TLS: Python SSL support not installed"))
if smtps:
ui.note(_('(using smtps)\n'))
--- a/mercurial/match.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/match.py Wed Sep 28 16:11:53 2011 -0500
@@ -49,7 +49,6 @@
'<something>' - a pattern of the specified default type
"""
- self._ctx = None
self._root = root
self._cwd = cwd
self._files = []
--- a/mercurial/mdiff.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/mdiff.py Wed Sep 28 16:11:53 2011 -0500
@@ -157,6 +157,7 @@
return 0
return ret
+ lastfunc = [0, '']
def yieldhunk(hunk):
(astart, a2, bstart, b2, delta) = hunk
aend = contextend(a2, len(l1))
@@ -165,13 +166,19 @@
func = ""
if opts.showfunc:
- # walk backwards from the start of the context
- # to find a line starting with an alphanumeric char.
- for x in xrange(astart - 1, -1, -1):
- t = l1[x].rstrip()
- if funcre.match(t):
- func = ' ' + t[:40]
+ lastpos, func = lastfunc
+ # walk backwards from the start of the context up to the start of
+ # the previous hunk context until we find a line starting with an
+ # alphanumeric char.
+ for i in xrange(astart - 1, lastpos - 1, -1):
+ if l1[i][0].isalnum():
+ func = ' ' + l1[i].rstrip()[:40]
+ lastfunc[1] = func
break
+ # by recording this hunk's starting point as the next place to
+ # start looking for function lines, we avoid reading any line in
+ # the file more than once.
+ lastfunc[0] = astart
yield "@@ -%d,%d +%d,%d @@%s\n" % (astart + 1, alen,
bstart + 1, blen, func)
@@ -180,9 +187,6 @@
for x in xrange(a2, aend):
yield ' ' + l1[x]
- if opts.showfunc:
- funcre = re.compile('\w')
-
# bdiff.blocks gives us the matching sequences in the files. The loop
# below finds the spaces between those matching sequences and translates
# them into diff output.
--- a/mercurial/merge.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/merge.py Wed Sep 28 16:11:53 2011 -0500
@@ -273,7 +273,6 @@
action.sort(key=actionkey)
# prescan for merges
- u = repo.ui
for a in action:
f, m = a[:2]
if m == 'm': # merge
@@ -308,8 +307,8 @@
numupdates = len(action)
for i, a in enumerate(action):
f, m = a[:2]
- u.progress(_('updating'), i + 1, item=f, total=numupdates,
- unit=_('files'))
+ repo.ui.progress(_('updating'), i + 1, item=f, total=numupdates,
+ unit=_('files'))
if f and f[0] == "/":
continue
if m == "r": # remove
@@ -377,7 +376,7 @@
repo.wopener.audit(f)
util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
ms.commit()
- u.progress(_('updating'), None, total=numupdates, unit=_('files'))
+ repo.ui.progress(_('updating'), None, total=numupdates, unit=_('files'))
return updated, merged, removed, unresolved
--- a/mercurial/minirst.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/minirst.py Wed Sep 28 16:11:53 2011 -0500
@@ -18,17 +18,14 @@
when adding support for new constructs.
"""
-import re, sys
+import re
import util, encoding
from i18n import _
-
def replace(text, substs):
- utext = text.decode(encoding.encoding)
for f, t in substs:
- utext = utext.replace(f, t)
- return utext.encode(encoding.encoding)
-
+ text = text.replace(f, t)
+ return text
_blockre = re.compile(r"\n(?:\s*\n)+")
@@ -39,14 +36,14 @@
has an 'indent' field and a 'lines' field.
"""
blocks = []
- for b in _blockre.split(text.strip()):
+ for b in _blockre.split(text.lstrip('\n').rstrip()):
lines = b.splitlines()
- indent = min((len(l) - len(l.lstrip())) for l in lines)
- lines = [l[indent:] for l in lines]
- blocks.append(dict(indent=indent, lines=lines))
+ if lines:
+ indent = min((len(l) - len(l.lstrip())) for l in lines)
+ lines = [l[indent:] for l in lines]
+ blocks.append(dict(indent=indent, lines=lines))
return blocks
-
def findliteralblocks(blocks):
"""Finds literal blocks and adds a 'type' field to the blocks.
@@ -103,6 +100,7 @@
r'((.*) +)(.*)$')
_fieldre = re.compile(r':(?![: ])([^:]*)(?<! ):[ ]+(.*)')
_definitionre = re.compile(r'[^ ]')
+_tablere = re.compile(r'(=+\s+)*=+')
def splitparagraphs(blocks):
"""Split paragraphs into lists."""
@@ -146,7 +144,6 @@
i += 1
return blocks
-
_fieldwidth = 12
def updatefieldlists(blocks):
@@ -173,7 +170,6 @@
return blocks
-
def updateoptionlists(blocks):
i = 0
while i < len(blocks):
@@ -238,18 +234,67 @@
# Always delete "..container:: type" block
del blocks[i]
j = i
+ i -= 1
while j < len(blocks) and blocks[j]['indent'] > indent:
if prune:
del blocks[j]
- i -= 1 # adjust outer index
else:
blocks[j]['indent'] -= adjustment
j += 1
i += 1
return blocks, pruned
+_sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
-_sectionre = re.compile(r"""^([-=`:.'"~^_*+#])\1+$""")
+def findtables(blocks):
+ '''Find simple tables
+
+ Only simple one-line table elements are supported
+ '''
+
+ for block in blocks:
+ # Searching for a block that looks like this:
+ #
+ # === ==== ===
+ # A B C
+ # === ==== === <- optional
+ # 1 2 3
+ # x y z
+ # === ==== ===
+ if (block['type'] == 'paragraph' and
+ len(block['lines']) > 4 and
+ _tablere.match(block['lines'][0]) and
+ block['lines'][0] == block['lines'][-1]):
+ block['type'] = 'table'
+ block['header'] = False
+ div = block['lines'][0]
+
+ # column markers are ASCII so we can calculate column
+ # position in bytes
+ columns = [x for x in xrange(len(div))
+ if div[x] == '=' and (x == 0 or div[x - 1] == ' ')]
+ rows = []
+ for l in block['lines'][1:-1]:
+ if l == div:
+ block['header'] = True
+ continue
+ row = []
+ # we measure columns not in bytes or characters but in
+ # colwidth which makes things tricky
+ pos = columns[0] # leading whitespace is bytes
+ for n, start in enumerate(columns):
+ if n + 1 < len(columns):
+ width = columns[n + 1] - start
+ v = encoding.getcols(l, pos, width) # gather columns
+ pos += len(v) # calculate byte position of end
+ row.append(v.strip())
+ else:
+ row.append(l[pos:].strip())
+ rows.append(row)
+
+ block['table'] = rows
+
+ return blocks
def findsections(blocks):
"""Finds sections.
@@ -273,7 +318,6 @@
del block['lines'][1]
return blocks
-
def inlineliterals(blocks):
substs = [('``', '"')]
for b in blocks:
@@ -281,7 +325,6 @@
b['lines'] = [replace(l, substs) for l in b['lines']]
return blocks
-
def hgrole(blocks):
substs = [(':hg:`', '"hg '), ('`', '"')]
for b in blocks:
@@ -293,7 +336,6 @@
b['lines'] = [replace(l, substs) for l in b['lines']]
return blocks
-
def addmargins(blocks):
"""Adds empty blocks for vertical spacing.
@@ -366,7 +408,7 @@
hanging = block['optstrwidth']
initindent = '%s%s ' % (block['optstr'], ' ' * ((hanging - colwidth)))
hangindent = ' ' * (encoding.colwidth(initindent) + 1)
- return ' %s' % (util.wrap(desc, usablewidth,
+ return ' %s\n' % (util.wrap(desc, usablewidth,
initindent=initindent,
hangindent=hangindent))
@@ -381,25 +423,47 @@
defindent = indent + hang * ' '
text = ' '.join(map(str.strip, block['lines']))
- return '%s\n%s' % (indent + admonition, util.wrap(text, width=width,
- initindent=defindent,
- hangindent=defindent))
+ return '%s\n%s\n' % (indent + admonition,
+ util.wrap(text, width=width,
+ initindent=defindent,
+ hangindent=defindent))
if block['type'] == 'margin':
- return ''
+ return '\n'
if block['type'] == 'literal':
indent += ' '
- return indent + ('\n' + indent).join(block['lines'])
+ return indent + ('\n' + indent).join(block['lines']) + '\n'
if block['type'] == 'section':
underline = encoding.colwidth(block['lines'][0]) * block['underline']
- return "%s%s\n%s%s" % (indent, block['lines'][0],indent, underline)
+ return "%s%s\n%s%s\n" % (indent, block['lines'][0],indent, underline)
+ if block['type'] == 'table':
+ table = block['table']
+ # compute column widths
+ widths = [max([encoding.colwidth(e) for e in c]) for c in zip(*table)]
+ text = ''
+ span = sum(widths) + len(widths) - 1
+ indent = ' ' * block['indent']
+ hang = ' ' * (len(indent) + span - widths[-1])
+
+ for row in table:
+ l = []
+ for w, v in zip(widths, row):
+ pad = ' ' * (w - encoding.colwidth(v))
+ l.append(v + pad)
+ l = ' '.join(l)
+ l = util.wrap(l, width=width, initindent=indent, hangindent=hang)
+ if not text and block['header']:
+ text = l + '\n' + indent + '-' * (min(width, span)) + '\n'
+ else:
+ text += l + "\n"
+ return text
if block['type'] == 'definition':
term = indent + block['lines'][0]
hang = len(block['lines'][-1]) - len(block['lines'][-1].lstrip())
defindent = indent + hang * ' '
text = ' '.join(map(str.strip, block['lines'][1:]))
- return '%s\n%s' % (term, util.wrap(text, width=width,
- initindent=defindent,
- hangindent=defindent))
+ return '%s\n%s\n' % (term, util.wrap(text, width=width,
+ initindent=defindent,
+ hangindent=defindent))
subindent = indent
if block['type'] == 'bullet':
if block['lines'][0].startswith('| '):
@@ -431,15 +495,16 @@
text = ' '.join(map(str.strip, block['lines']))
return util.wrap(text, width=width,
initindent=indent,
- hangindent=subindent)
-
+ hangindent=subindent) + '\n'
-def format(text, width, indent=0, keep=None):
- """Parse and format the text according to width."""
+def parse(text, indent=0, keep=None):
+ """Parse text into a list of blocks"""
+ pruned = []
blocks = findblocks(text)
for b in blocks:
b['indent'] += indent
blocks = findliteralblocks(blocks)
+ blocks = findtables(blocks)
blocks, pruned = prunecontainers(blocks, keep or [])
blocks = findsections(blocks)
blocks = inlineliterals(blocks)
@@ -450,33 +515,65 @@
blocks = addmargins(blocks)
blocks = prunecomments(blocks)
blocks = findadmonitions(blocks)
- text = '\n'.join(formatblock(b, width) for b in blocks)
+ return blocks, pruned
+
+def formatblocks(blocks, width):
+ text = ''.join(formatblock(b, width) for b in blocks)
+ return text
+
+def format(text, width, indent=0, keep=None):
+ """Parse and format the text according to width."""
+ blocks, pruned = parse(text, indent, keep or [])
+ text = ''.join(formatblock(b, width) for b in blocks)
if keep is None:
return text
else:
return text, pruned
-
-if __name__ == "__main__":
- from pprint import pprint
-
- def debug(func, *args):
- blocks = func(*args)
- print "*** after %s:" % func.__name__
- pprint(blocks)
- print
- return blocks
+def getsections(blocks):
+ '''return a list of (section name, nesting level, blocks) tuples'''
+ nest = ""
+ level = 0
+ secs = []
+ for b in blocks:
+ if b['type'] == 'section':
+ i = b['underline']
+ if i not in nest:
+ nest += i
+ level = nest.index(i) + 1
+ nest = nest[:level]
+ secs.append((b['lines'][0], level, [b]))
+ else:
+ if not secs:
+ # add an initial empty section
+ secs = [('', 0, [])]
+ secs[-1][2].append(b)
+ return secs
- text = sys.stdin.read()
- blocks = debug(findblocks, text)
- blocks = debug(findliteralblocks, blocks)
- blocks, pruned = debug(prunecontainers, blocks, sys.argv[1:])
- blocks = debug(inlineliterals, blocks)
- blocks = debug(splitparagraphs, blocks)
- blocks = debug(updatefieldlists, blocks)
- blocks = debug(updateoptionlists, blocks)
- blocks = debug(findsections, blocks)
- blocks = debug(addmargins, blocks)
- blocks = debug(prunecomments, blocks)
- blocks = debug(findadmonitions, blocks)
- print '\n'.join(formatblock(b, 30) for b in blocks)
+def decorateblocks(blocks, width):
+ '''generate a list of (section name, line text) pairs for search'''
+ lines = []
+ for s in getsections(blocks):
+ section = s[0]
+ text = formatblocks(s[2], width)
+ lines.append([(section, l) for l in text.splitlines(True)])
+ return lines
+
+def maketable(data, indent=0, header=False):
+ '''Generate an RST table for the given table data'''
+
+ widths = [max(encoding.colwidth(e) for e in c) for c in zip(*data)]
+ indent = ' ' * indent
+ div = indent + ' '.join('=' * w for w in widths) + '\n'
+
+ out = [div]
+ for row in data:
+ l = []
+ for w, v in zip(widths, row):
+ pad = ' ' * (w - encoding.colwidth(v))
+ l.append(v + pad)
+ out.append(indent + ' '.join(l) + "\n")
+ if header and len(data) > 1:
+ out.insert(2, div)
+ out.append(div)
+ return ''.join(out)
--- a/mercurial/osutil.c Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/osutil.c Wed Sep 28 16:11:53 2011 -0500
@@ -12,6 +12,7 @@
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
+#include <errno.h>
#ifdef _WIN32
#include <windows.h>
@@ -288,7 +289,8 @@
#endif
if (pathlen >= PATH_MAX) {
- PyErr_SetString(PyExc_ValueError, "path too long");
+ errno = ENAMETOOLONG;
+ PyErr_SetFromErrnoWithFilename(PyExc_OSError, path);
goto error_value;
}
strncpy(fullpath, path, PATH_MAX);
--- a/mercurial/patch.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/patch.py Wed Sep 28 16:11:53 2011 -0500
@@ -126,7 +126,7 @@
mimeheaders = ['content-type']
- if not hasattr(stream, 'next'):
+ if not util.safehasattr(stream, 'next'):
# http responses, for example, have readline but not next
stream = fiter(stream)
--- a/mercurial/posix.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/posix.py Wed Sep 28 16:11:53 2011 -0500
@@ -84,6 +84,21 @@
# Turn off all +x bits
os.chmod(f, s & 0666)
+def copymode(src, dst, mode=None):
+ '''Copy the file mode from the file at path src to dst.
+ If src doesn't exist, we're using mode instead. If mode is None, we're
+ using umask.'''
+ try:
+ st_mode = os.lstat(src).st_mode & 0777
+ except OSError, inst:
+ if inst.errno != errno.ENOENT:
+ raise
+ st_mode = mode
+ if st_mode is None:
+ st_mode = ~umask
+ st_mode &= 0666
+ os.chmod(dst, st_mode)
+
def checkexec(path):
"""
Check whether the given path is on a filesystem with UNIX-like exec flags
@@ -241,7 +256,9 @@
for path in os.environ.get('PATH', '').split(os.pathsep):
executable = findexisting(os.path.join(path, command))
if executable is not None:
- return executable
+ st = os.stat(executable)
+ if (st.st_mode & (stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)):
+ return executable
return None
def setsignalhandler():
@@ -325,3 +342,45 @@
except ImportError:
pass
return 80
+
+def makedir(path, notindexed):
+ os.mkdir(path)
+
+def unlinkpath(f):
+ """unlink and remove the directory if it is empty"""
+ os.unlink(f)
+ # try removing directories that might now be empty
+ try:
+ os.removedirs(os.path.dirname(f))
+ except OSError:
+ pass
+
+def lookupreg(key, name=None, scope=None):
+ return None
+
+def hidewindow():
+ """Hide current shell window.
+
+ Used to hide the window opened when starting asynchronous
+ child process under Windows, unneeded on other systems.
+ """
+ pass
+
+class cachestat(object):
+ def __init__(self, path):
+ self.stat = os.stat(path)
+
+ def cacheable(self):
+ return bool(self.stat.st_ino)
+
+ def __eq__(self, other):
+ try:
+ return self.stat == other.stat
+ except AttributeError:
+ return False
+
+ def __ne__(self, other):
+ return not self == other
+
+def executablepath():
+ return None # available on Windows only
--- a/mercurial/pure/parsers.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/pure/parsers.py Wed Sep 28 16:11:53 2011 -0500
@@ -36,7 +36,7 @@
s = struct.calcsize(indexformatng)
index = []
cache = None
- n = off = 0
+ off = 0
l = len(data) - s
append = index.append
@@ -45,7 +45,6 @@
while off <= l:
e = _unpack(indexformatng, data[off:off + s])
append(e)
- n += 1
if e[1] < 0:
break
off += e[1] + s
@@ -53,7 +52,6 @@
while off <= l:
e = _unpack(indexformatng, data[off:off + s])
append(e)
- n += 1
off += s
if off != len(data):
--- a/mercurial/repair.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/repair.py Wed Sep 28 16:11:53 2011 -0500
@@ -11,9 +11,8 @@
from mercurial.i18n import _
import os
-def _bundle(repo, bases, heads, node, suffix, compress=True):
+def _bundle(repo, cg, node, suffix, compress=True):
"""create a bundle with the specified revisions as a backup"""
- cg = repo.changegroupsubset(bases, heads, 'strip')
backupdir = repo.join("strip-backup")
if not os.path.isdir(backupdir):
os.mkdir(backupdir)
@@ -83,11 +82,9 @@
saveheads.add(r)
saveheads = [cl.node(r) for r in saveheads]
- # compute base nodes
- if saverevs:
- descendants = set(cl.descendants(*saverevs))
- saverevs.difference_update(descendants)
- savebases = [cl.node(r) for r in saverevs]
+ # compute common nodes
+ savecommon = set(cl.node(p) for r in saverevs for p in cl.parentrevs(r)
+ if p not in saverevs and p not in tostrip)
bm = repo._bookmarks
updatebm = []
@@ -99,12 +96,14 @@
# create a changegroup for all the branches we need to keep
backupfile = None
if backup == "all":
- backupfile = _bundle(repo, [node], cl.heads(), node, 'backup')
+ allnodes=[cl.node(r) for r in xrange(striprev, len(cl))]
+ cg = repo._changegroup(allnodes, 'strip')
+ backupfile = _bundle(repo, cg, node, 'backup')
repo.ui.status(_("saved backup bundle to %s\n") % backupfile)
- if saveheads or savebases:
+ if saveheads or savecommon:
# do not compress partial bundle if we remove it from disk later
- chgrpfile = _bundle(repo, savebases, saveheads, node, 'temp',
- compress=keeppartialbundle)
+ cg = repo.getbundle('strip', common=savecommon, heads=saveheads)
+ chgrpfile = _bundle(repo, cg, node, 'temp', compress=keeppartialbundle)
mfst = repo.manifest
@@ -128,7 +127,7 @@
tr.abort()
raise
- if saveheads or savebases:
+ if saveheads or savecommon:
ui.note(_("adding branch\n"))
f = open(chgrpfile, "rb")
gen = changegroup.readbundle(f, chgrpfile)
--- a/mercurial/revlog.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/revlog.py Wed Sep 28 16:11:53 2011 -0500
@@ -226,9 +226,10 @@
self._nodepos = None
v = REVLOG_DEFAULT_VERSION
- if hasattr(opener, 'options'):
- if 'revlogv1' in opener.options:
- if 'generaldelta' in opener.options:
+ opts = getattr(opener, 'options', None)
+ if opts is not None:
+ if 'revlogv1' in opts:
+ if 'generaldelta' in opts:
v |= REVLOGGENERALDELTA
else:
v = 0
@@ -945,9 +946,9 @@
e = self._io.packentry(self.index[i], self.node, self.version, i)
fp.write(e)
- # if we don't call rename, the temp file will never replace the
+ # if we don't call close, the temp file will never replace the
# real index
- fp.rename()
+ fp.close()
tr.replace(self.indexfile, trindex * self._io.size)
self._chunkclear()
--- a/mercurial/revset.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/revset.py Wed Sep 28 16:11:53 2011 -0500
@@ -6,7 +6,7 @@
# GNU General Public License version 2 or any later version.
import re
-import parser, util, error, discovery, hbisect
+import parser, util, error, discovery, hbisect, node
import bookmarks as bookmarksmod
import match as matchmod
from i18n import _
@@ -235,15 +235,24 @@
n = getstring(x, _("author requires a string")).lower()
return [r for r in subset if n in repo[r].user().lower()]
-def bisected(repo, subset, x):
- """``bisected(string)``
- Changesets marked in the specified bisect state (good, bad, skip).
+def bisect(repo, subset, x):
+ """``bisect(string)``
+ Changesets marked in the specified bisect status:
+
+ - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
+ - ``goods``, ``bads`` : csets topologicaly good/bad
+ - ``range`` : csets taking part in the bisection
+ - ``pruned`` : csets that are goods, bads or skipped
+ - ``untested`` : csets whose fate is yet unknown
+ - ``ignored`` : csets ignored due to DAG topology
"""
- state = getstring(x, _("bisect requires a string")).lower()
- if state not in ('good', 'bad', 'skip', 'unknown'):
- raise error.ParseError(_('invalid bisect state'))
- marked = set(repo.changelog.rev(n) for n in hbisect.load_state(repo)[state])
- return [r for r in subset if r in marked]
+ status = getstring(x, _("bisect requires a string")).lower()
+ return [r for r in subset if r in hbisect.get(repo, status)]
+
+# Backward-compatibility
+# - no help entry so that we do not advertise it any more
+def bisected(repo, subset, x):
+ return bisect(repo, subset, x)
def bookmark(repo, subset, x):
"""``bookmark([name])``
@@ -407,6 +416,12 @@
return [r for r in subset if r in s]
+def first(repo, subset, x):
+ """``first(set, [n])``
+ An alias for limit().
+ """
+ return limit(repo, subset, x)
+
def follow(repo, subset, x):
"""``follow([file])``
An alias for ``::.`` (ancestors of the working copy's first parent).
@@ -513,14 +528,16 @@
return l
def limit(repo, subset, x):
- """``limit(set, n)``
- First n members of set.
+ """``limit(set, [n])``
+ First n members of set, defaulting to 1.
"""
# i18n: "limit" is a keyword
- l = getargs(x, 2, 2, _("limit requires two arguments"))
+ l = getargs(x, 1, 2, _("limit requires one or two arguments"))
try:
- # i18n: "limit" is a keyword
- lim = int(getstring(l[1], _("limit requires a number")))
+ lim = 1
+ if len(l) == 2:
+ # i18n: "limit" is a keyword
+ lim = int(getstring(l[1], _("limit requires a number")))
except (TypeError, ValueError):
# i18n: "limit" is a keyword
raise error.ParseError(_("limit expects a number"))
@@ -529,14 +546,16 @@
return [r for r in os if r in ss]
def last(repo, subset, x):
- """``last(set, n)``
- Last n members of set.
+ """``last(set, [n])``
+ Last n members of set, defaulting to 1.
"""
# i18n: "last" is a keyword
- l = getargs(x, 2, 2, _("last requires two arguments"))
+ l = getargs(x, 1, 2, _("last requires one or two arguments"))
try:
- # i18n: "last" is a keyword
- lim = int(getstring(l[1], _("last requires a number")))
+ lim = 1
+ if len(l) == 2:
+ # i18n: "last" is a keyword
+ lim = int(getstring(l[1], _("last requires a number")))
except (TypeError, ValueError):
# i18n: "last" is a keyword
raise error.ParseError(_("last expects a number"))
@@ -827,6 +846,7 @@
"ancestor": ancestor,
"ancestors": ancestors,
"author": author,
+ "bisect": bisect,
"bisected": bisected,
"bookmark": bookmark,
"branch": branch,
@@ -838,6 +858,7 @@
"descendants": descendants,
"file": hasfile,
"filelog": filelog,
+ "first": first,
"follow": follow,
"grep": grep,
"head": head,
@@ -951,7 +972,7 @@
w = 100 # very slow
elif f == "ancestor":
w = 1 * smallbonus
- elif f in "reverse limit":
+ elif f in "reverse limit first":
w = 0
elif f in "sort":
w = 10 # assume most sorts look at changelog
@@ -1019,11 +1040,81 @@
tree, pos = parse(spec)
if (pos != len(spec)):
raise error.ParseError(_("invalid token"), pos)
- tree = findaliases(ui, tree)
+ if ui:
+ tree = findaliases(ui, tree)
weight, tree = optimize(tree, True)
def mfunc(repo, subset):
return getset(repo, subset, tree)
return mfunc
+def formatspec(expr, *args):
+ '''
+ This is a convenience function for using revsets internally, and
+ escapes arguments appropriately. Aliases are intentionally ignored
+ so that intended expression behavior isn't accidentally subverted.
+
+ Supported arguments:
+
+ %d = int(arg), no quoting
+ %s = string(arg), escaped and single-quoted
+ %b = arg.branch(), escaped and single-quoted
+ %n = hex(arg), single-quoted
+ %% = a literal '%'
+
+ Prefixing the type with 'l' specifies a list of that type.
+
+ >>> formatspec('%d:: and not %d::', 10, 20)
+ '10:: and not 20::'
+ >>> formatspec('keyword(%s)', 'foo\\xe9')
+ "keyword('foo\\\\xe9')"
+ >>> b = lambda: 'default'
+ >>> b.branch = b
+ >>> formatspec('branch(%b)', b)
+ "branch('default')"
+ >>> formatspec('root(%ls)', ['a', 'b', 'c', 'd'])
+ "root(('a' or 'b' or 'c' or 'd'))"
+ '''
+
+ def quote(s):
+ return repr(str(s))
+
+ def argtype(c, arg):
+ if c == 'd':
+ return str(int(arg))
+ elif c == 's':
+ return quote(arg)
+ elif c == 'n':
+ return quote(node.hex(arg))
+ elif c == 'b':
+ return quote(arg.branch())
+
+ ret = ''
+ pos = 0
+ arg = 0
+ while pos < len(expr):
+ c = expr[pos]
+ if c == '%':
+ pos += 1
+ d = expr[pos]
+ if d == '%':
+ ret += d
+ elif d in 'dsnb':
+ ret += argtype(d, args[arg])
+ arg += 1
+ elif d == 'l':
+ # a list of some type
+ pos += 1
+ d = expr[pos]
+ lv = ' or '.join(argtype(d, e) for e in args[arg])
+ ret += '(%s)' % lv
+ arg += 1
+ else:
+ raise util.Abort('unexpected revspec format character %s' % d)
+ else:
+ ret += c
+ pos += 1
+
+ return ret
+
# tell hggettext to extract docstrings from these functions:
i18nfunctions = symbols.values()
--- a/mercurial/scmutil.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/scmutil.py Wed Sep 28 16:11:53 2011 -0500
@@ -324,10 +324,10 @@
def errhandler(err):
if err.filename == path:
raise err
- if followsym and hasattr(os.path, 'samestat'):
+ samestat = getattr(os.path, 'samestat', None)
+ if followsym and samestat is not None:
def adddir(dirlst, dirname):
match = False
- samestat = os.path.samestat
dirstat = os.stat(dirname)
for lstdirstat in dirlst:
if samestat(dirstat, lstdirstat):
@@ -709,3 +709,95 @@
raise error.RequirementError(_("unknown repository format: "
"requires features '%s' (upgrade Mercurial)") % "', '".join(missings))
return requirements
+
+class filecacheentry(object):
+ def __init__(self, path):
+ self.path = path
+ self.cachestat = filecacheentry.stat(self.path)
+
+ if self.cachestat:
+ self._cacheable = self.cachestat.cacheable()
+ else:
+ # None means we don't know yet
+ self._cacheable = None
+
+ def refresh(self):
+ if self.cacheable():
+ self.cachestat = filecacheentry.stat(self.path)
+
+ def cacheable(self):
+ if self._cacheable is not None:
+ return self._cacheable
+
+ # we don't know yet, assume it is for now
+ return True
+
+ def changed(self):
+ # no point in going further if we can't cache it
+ if not self.cacheable():
+ return True
+
+ newstat = filecacheentry.stat(self.path)
+
+ # we may not know if it's cacheable yet, check again now
+ if newstat and self._cacheable is None:
+ self._cacheable = newstat.cacheable()
+
+ # check again
+ if not self._cacheable:
+ return True
+
+ if self.cachestat != newstat:
+ self.cachestat = newstat
+ return True
+ else:
+ return False
+
+ @staticmethod
+ def stat(path):
+ try:
+ return util.cachestat(path)
+ except OSError, e:
+ if e.errno != errno.ENOENT:
+ raise
+
+class filecache(object):
+ '''A property like decorator that tracks a file under .hg/ for updates.
+
+ Records stat info when called in _filecache.
+
+ On subsequent calls, compares old stat info with new info, and recreates
+ the object when needed, updating the new stat info in _filecache.
+
+ Mercurial either atomic renames or appends for files under .hg,
+ so to ensure the cache is reliable we need the filesystem to be able
+ to tell us if a file has been replaced. If it can't, we fallback to
+ recreating the object on every call (essentially the same behaviour as
+ propertycache).'''
+ def __init__(self, path, instore=False):
+ self.path = path
+ self.instore = instore
+
+ def __call__(self, func):
+ self.func = func
+ self.name = func.__name__
+ return self
+
+ def __get__(self, obj, type=None):
+ entry = obj._filecache.get(self.name)
+
+ if entry:
+ if entry.changed():
+ entry.obj = self.func(obj)
+ else:
+ path = self.instore and obj.sjoin(self.path) or obj.join(self.path)
+
+ # We stat -before- creating the object so our cache doesn't lie if
+ # a writer modified between the time we read and stat
+ entry = filecacheentry(path)
+ entry.obj = self.func(obj)
+
+ obj._filecache[self.name] = entry
+
+ setattr(obj, self.name, entry.obj)
+ return entry.obj
--- a/mercurial/simplemerge.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/simplemerge.py Wed Sep 28 16:11:53 2011 -0500
@@ -445,7 +445,7 @@
out.write(line)
if not opts.get('print'):
- out.rename()
+ out.close()
if m3.conflicts:
if not opts.get('quiet'):
--- a/mercurial/sshrepo.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/sshrepo.py Wed Sep 28 16:11:53 2011 -0500
@@ -164,6 +164,17 @@
def _recv(self):
l = self.pipei.readline()
+ if l == '\n':
+ err = []
+ while True:
+ line = self.pipee.readline()
+ if line == '-\n':
+ break
+ err.extend([line])
+ if len(err) > 0:
+ # strip the trailing newline added to the last line server-side
+ err[-1] = err[-1][:-1]
+ self._abort(error.OutOfBandError(*err))
self.readerr()
try:
l = int(l)
--- a/mercurial/sshserver.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/sshserver.py Wed Sep 28 16:11:53 2011 -0500
@@ -82,6 +82,12 @@
def sendpusherror(self, rsp):
self.sendresponse(rsp.res)
+ def sendooberror(self, rsp):
+ self.ui.ferr.write('%s\n-\n' % rsp.message)
+ self.ui.ferr.flush()
+ self.fout.write('\n')
+ self.fout.flush()
+
def serve_forever(self):
try:
while self.serve_one():
@@ -96,6 +102,7 @@
wireproto.streamres: sendstream,
wireproto.pushres: sendpushresponse,
wireproto.pusherr: sendpusherror,
+ wireproto.ooberror: sendooberror,
}
def serve_one(self):
--- a/mercurial/sslutil.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/sslutil.py Wed Sep 28 16:11:53 2011 -0500
@@ -22,6 +22,8 @@
def ssl_wrap_socket(sock, key_file, cert_file,
cert_reqs=CERT_REQUIRED, ca_certs=None):
+ if not util.safehasattr(socket, 'ssl'):
+ raise util.Abort(_('Python SSL support not found'))
if ca_certs:
raise util.Abort(_(
'certificate checking requires Python 2.6'))
--- a/mercurial/statichttprepo.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/statichttprepo.py Wed Sep 28 16:11:53 2011 -0500
@@ -31,15 +31,11 @@
try:
f = self.opener.open(req)
data = f.read()
- if hasattr(f, 'getcode'):
- # python 2.6+
- code = f.getcode()
- elif hasattr(f, 'code'):
- # undocumented attribute, seems to be set in 2.4 and 2.5
- code = f.code
- else:
- # Don't know how to check, hope for the best.
- code = 206
+ # Python 2.6+ defines a getcode() function, and 2.4 and
+ # 2.5 appear to always have an undocumented code attribute
+ # set. If we can't read either of those, fall back to 206
+ # and hope for the best.
+ code = getattr(f, 'getcode', lambda : getattr(f, 'code', 206))()
except urllib2.HTTPError, inst:
num = inst.code == 404 and errno.ENOENT or None
raise IOError(num, inst)
@@ -125,6 +121,7 @@
self.encodepats = None
self.decodepats = None
self.capabilities.difference_update(["pushkey"])
+ self._filecache = {}
def url(self):
return self._url
--- a/mercurial/store.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/store.py Wed Sep 28 16:11:53 2011 -0500
@@ -345,7 +345,7 @@
fp = self.opener('fncache', mode='wb', atomictemp=True)
for p in self.entries:
fp.write(encodedir(p) + '\n')
- fp.rename()
+ fp.close()
self._dirty = False
def add(self, fn):
--- a/mercurial/subrepo.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/subrepo.py Wed Sep 28 16:11:53 2011 -0500
@@ -50,15 +50,7 @@
if err.errno != errno.ENOENT:
raise
- state = {}
- for path, src in p[''].items():
- kind = 'hg'
- if src.startswith('['):
- if ']' not in src:
- raise util.Abort(_('missing ] in subrepo source'))
- kind, src = src.split(']', 1)
- kind = kind[1:]
-
+ def remap(src):
for pattern, repl in p.items('subpaths'):
# Turn r'C:\foo\bar' into r'C:\\foo\\bar' since re.sub
# does a string decode.
@@ -72,7 +64,34 @@
except re.error, e:
raise util.Abort(_("bad subrepository pattern in %s: %s")
% (p.source('subpaths', pattern), e))
+ return src
+ state = {}
+ for path, src in p[''].items():
+ kind = 'hg'
+ if src.startswith('['):
+ if ']' not in src:
+ raise util.Abort(_('missing ] in subrepo source'))
+ kind, src = src.split(']', 1)
+ kind = kind[1:]
+ src = src.lstrip() # strip any extra whitespace after ']'
+
+ if not util.url(src).isabs():
+ parent = _abssource(ctx._repo, abort=False)
+ if parent:
+ parent = util.url(parent)
+ parent.path = posixpath.join(parent.path or '', src)
+ parent.path = posixpath.normpath(parent.path)
+ joined = str(parent)
+ # Remap the full joined path and use it if it changes,
+ # else remap the original source.
+ remapped = remap(joined)
+ if remapped == joined:
+ src = remap(src)
+ else:
+ src = remapped
+
+ src = remap(src)
state[path] = (src.strip(), rev.get(path, ''), kind)
return state
@@ -181,22 +200,22 @@
def reporelpath(repo):
"""return path to this (sub)repo as seen from outermost repo"""
parent = repo
- while hasattr(parent, '_subparent'):
+ while util.safehasattr(parent, '_subparent'):
parent = parent._subparent
return repo.root[len(parent.root)+1:]
def subrelpath(sub):
"""return path to this subrepo as seen from outermost repo"""
- if hasattr(sub, '_relpath'):
+ if util.safehasattr(sub, '_relpath'):
return sub._relpath
- if not hasattr(sub, '_repo'):
+ if not util.safehasattr(sub, '_repo'):
return sub._path
return reporelpath(sub._repo)
def _abssource(repo, push=False, abort=True):
"""return pull/push path of repo - either based on parent repo .hgsub info
or on the top repo config. Abort or return None if no source found."""
- if hasattr(repo, '_subparent'):
+ if util.safehasattr(repo, '_subparent'):
source = util.url(repo._subsource)
if source.isabs():
return str(source)
@@ -208,7 +227,7 @@
parent.path = posixpath.normpath(parent.path)
return str(parent)
else: # recursion reached top repo
- if hasattr(repo, '_subtoppath'):
+ if util.safehasattr(repo, '_subtoppath'):
return repo._subtoppath
if push and repo.ui.config('paths', 'default-push'):
return repo.ui.config('paths', 'default-push')
--- a/mercurial/tags.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/tags.py Wed Sep 28 16:11:53 2011 -0500
@@ -287,6 +287,6 @@
cachefile.write("%s %s\n" % (hex(node), name))
try:
- cachefile.rename()
+ cachefile.close()
except (OSError, IOError):
pass
--- a/mercurial/templatefilters.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templatefilters.py Wed Sep 28 16:11:53 2011 -0500
@@ -7,6 +7,7 @@
import cgi, re, os, time, urllib
import encoding, node, util
+import hbisect
def addbreaks(text):
""":addbreaks: Any text. Add an XHTML "<br />" tag before the end of
@@ -188,13 +189,13 @@
return '"%s"' % jsonescape(u)
elif isinstance(obj, unicode):
return '"%s"' % jsonescape(obj)
- elif hasattr(obj, 'keys'):
+ elif util.safehasattr(obj, 'keys'):
out = []
for k, v in obj.iteritems():
s = '%s: %s' % (json(k), json(v))
out.append(s)
return '{' + ', '.join(out) + '}'
- elif hasattr(obj, '__iter__'):
+ elif util.safehasattr(obj, '__iter__'):
out = []
for i in obj:
out.append(json(i))
@@ -268,6 +269,14 @@
"""
return text[:12]
+def shortbisect(text):
+ """:shortbisect: Any text. Treats `text` as a bisection status, and
+ returns a single-character representing the status (G: good, B: bad,
+ S: skipped, U: untested, I: ignored). Returns single space if `text`
+ is not a valid bisection status.
+ """
+ return hbisect.shortlabel(text) or ' '
+
def shortdate(text):
""":shortdate: Date. Returns a date like "2006-09-18"."""
return util.shortdate(text)
@@ -279,7 +288,7 @@
""":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):
+ if util.safehasattr(thing, '__iter__') and not isinstance(thing, str):
return "".join([stringify(t) for t in thing if t is not None])
return str(thing)
@@ -347,6 +356,7 @@
"rfc3339date": rfc3339date,
"rfc822date": rfc822date,
"short": short,
+ "shortbisect": shortbisect,
"shortdate": shortdate,
"stringescape": stringescape,
"stringify": stringify,
--- a/mercurial/templatekw.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templatekw.py Wed Sep 28 16:11:53 2011 -0500
@@ -7,6 +7,7 @@
from node import hex
import patch, util, error
+import hbisect
def showlist(name, values, plural=None, **args):
'''expand set of values.
@@ -145,6 +146,10 @@
""":author: String. The unmodified author of the changeset."""
return ctx.user()
+def showbisect(repo, ctx, templ, **args):
+ """:bisect: String. The changeset bisection status."""
+ return hbisect.label(repo, ctx.node())
+
def showbranch(**args):
""":branch: String. The name of the branch on which the changeset was
committed.
@@ -288,6 +293,7 @@
# revcache - a cache dictionary for the current revision
keywords = {
'author': showauthor,
+ 'bisect': showbisect,
'branch': showbranch,
'branches': showbranches,
'bookmarks': showbookmarks,
--- a/mercurial/templater.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templater.py Wed Sep 28 16:11:53 2011 -0500
@@ -135,7 +135,7 @@
v = mapping.get(key)
if v is None:
v = context._defaults.get(key, '')
- if hasattr(v, '__call__'):
+ if util.safehasattr(v, '__call__'):
return v(**mapping)
return v
@@ -172,14 +172,14 @@
def buildfunc(exp, context):
n = getsymbol(exp[1])
args = [compileexp(x, context) for x in getlist(exp[2])]
+ if n in funcs:
+ f = funcs[n]
+ return (f, args)
if n in context._filters:
if len(args) != 1:
raise error.ParseError(_("filter %s expects one argument") % n)
f = context._filters[n]
return (runfilter, (args[0][0], args[0][1], f))
- elif n in context._funcs:
- f = context._funcs[n]
- return (f, args)
methods = {
"string": lambda e, c: (runstring, e[1]),
@@ -191,6 +191,9 @@
"func": buildfunc,
}
+funcs = {
+}
+
# template engine
path = ['templates', '../templates']
@@ -200,14 +203,14 @@
'''yield a single stream from a possibly nested set of iterators'''
if isinstance(thing, str):
yield thing
- elif not hasattr(thing, '__iter__'):
+ elif not util.safehasattr(thing, '__iter__'):
if thing is not None:
yield str(thing)
else:
for i in thing:
if isinstance(i, str):
yield i
- elif not hasattr(i, '__iter__'):
+ elif not util.safehasattr(i, '__iter__'):
if i is not None:
yield str(i)
elif i is not None:
@@ -338,7 +341,7 @@
normpaths = []
# executable version (py2exe) doesn't support __file__
- if hasattr(sys, 'frozen'):
+ if util.mainfrozen():
module = sys.executable
else:
module = __file__
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/templates/map-cmdline.bisect Wed Sep 28 16:11:53 2011 -0500
@@ -0,0 +1,25 @@
+changeset = 'changeset: {rev}:{node|short}\nbisect: {bisect}\n{branches}{bookmarks}{tags}{parents}user: {author}\ndate: {date|date}\nsummary: {desc|firstline}\n\n'
+changeset_quiet = '{bisect|shortbisect} {rev}:{node|short}\n'
+changeset_verbose = 'changeset: {rev}:{node|short}\nbisect: {bisect}\n{branches}{bookmarks}{tags}{parents}user: {author}\ndate: {date|date}\n{files}{file_copies_switch}description:\n{desc|strip}\n\n\n'
+changeset_debug = 'changeset: {rev}:{node}\nbisect: {bisect}\n{branches}{bookmarks}{tags}{parents}{manifest}user: {author}\ndate: {date|date}\n{file_mods}{file_adds}{file_dels}{file_copies_switch}{extras}description:\n{desc|strip}\n\n\n'
+start_files = 'files: '
+file = ' {file}'
+end_files = '\n'
+start_file_mods = 'files: '
+file_mod = ' {file_mod}'
+end_file_mods = '\n'
+start_file_adds = 'files+: '
+file_add = ' {file_add}'
+end_file_adds = '\n'
+start_file_dels = 'files-: '
+file_del = ' {file_del}'
+end_file_dels = '\n'
+start_file_copies = 'copies: '
+file_copy = ' {name} ({source})'
+end_file_copies = '\n'
+parent = 'parent: {rev}:{node|formatnode}\n'
+manifest = 'manifest: {rev}:{node}\n'
+branch = 'branch: {branch}\n'
+tag = 'tag: {tag}\n'
+bookmark = 'bookmark: {bookmark}\n'
+extra = 'extra: {key}={value|stringescape}\n'
--- a/mercurial/templates/monoblue/footer.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/monoblue/footer.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -9,7 +9,7 @@
</div>
<div id="powered-by">
- <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
+ <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a></p>
</div>
<div id="corner-top-left"></div>
--- a/mercurial/templates/monoblue/index.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/monoblue/index.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -26,7 +26,7 @@
</div>
<div id="powered-by">
- <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a></p>
+ <p><a href="{logourl}" title="Mercurial"><img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a></p>
</div>
<div id="corner-top-left"></div>
--- a/mercurial/templates/paper/bookmarks.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/bookmarks.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -11,7 +11,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/branches.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/branches.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -11,7 +11,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/changeset.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/changeset.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -6,7 +6,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/error.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/error.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -7,7 +7,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/fileannotate.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/fileannotate.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -7,7 +7,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filediff.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/filediff.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -7,7 +7,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filelog.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/filelog.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -11,7 +11,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/filerevision.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/filerevision.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -7,7 +7,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/graph.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/graph.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -12,7 +12,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/help.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/help.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -11,7 +11,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/helptopics.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/helptopics.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -11,7 +11,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/index.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/index.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -6,7 +6,7 @@
<div class="container">
<div class="menu">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial" /></a>
</div>
<div class="main">
<h2>Mercurial Repositories</h2>
--- a/mercurial/templates/paper/manifest.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/manifest.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -7,7 +7,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog/{node|short}{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/search.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/search.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -7,7 +7,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a>
</div>
<ul>
<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/paper/shortlog.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/shortlog.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -11,7 +11,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li class="active">log</li>
--- a/mercurial/templates/paper/tags.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/paper/tags.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -11,7 +11,7 @@
<div class="menu">
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" alt="mercurial" /></a>
+<img src="{staticurl}{logoimg}" alt="mercurial" /></a>
</div>
<ul>
<li><a href="{url}shortlog{sessionvars%urlparameter}">log</a></li>
--- a/mercurial/templates/spartan/footer.tmpl Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/templates/spartan/footer.tmpl Wed Sep 28 16:11:53 2011 -0500
@@ -2,7 +2,7 @@
{motd}
<div class="logo">
<a href="{logourl}">
-<img src="{staticurl}hglogo.png" width=75 height=90 border=0 alt="mercurial"></a>
+<img src="{staticurl}{logoimg}" width=75 height=90 border=0 alt="mercurial"></a>
</div>
</body>
--- a/mercurial/ui.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/ui.py Wed Sep 28 16:11:53 2011 -0500
@@ -46,7 +46,7 @@
def copy(self):
return self.__class__(self)
- def _is_trusted(self, fp, f):
+ def _trusted(self, fp, f):
st = util.fstat(fp)
if util.isowner(st):
return True
@@ -75,7 +75,7 @@
raise
cfg = config.config()
- trusted = sections or trust or self._is_trusted(fp, filename)
+ trusted = sections or trust or self._trusted(fp, filename)
try:
cfg.read(filename, fp, sections=sections, remap=remap)
@@ -155,7 +155,19 @@
return self._data(untrusted).source(section, name) or 'none'
def config(self, section, name, default=None, untrusted=False):
- value = self._data(untrusted).get(section, name, default)
+ if isinstance(name, list):
+ alternates = name
+ else:
+ alternates = [name]
+
+ for n in alternates:
+ value = self._data(untrusted).get(section, name, None)
+ if value is not None:
+ name = n
+ break
+ else:
+ value = default
+
if self.debugflag and not untrusted and self._reportuntrusted:
uvalue = self._ucfg.get(section, name)
if uvalue is not None and uvalue != value:
@@ -164,12 +176,14 @@
return value
def configpath(self, section, name, default=None, untrusted=False):
- 'get a path config item, expanded relative to config file'
+ 'get a path config item, expanded relative to repo root or config file'
v = self.config(section, name, default, untrusted)
+ if v is None:
+ return None
if not os.path.isabs(v) or "://" not in v:
src = self.configsource(section, name, untrusted)
if ':' in src:
- base = os.path.dirname(src.rsplit(':'))
+ base = os.path.dirname(src.rsplit(':')[0])
v = os.path.join(base, os.path.expanduser(v))
return v
--- a/mercurial/url.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/url.py Wed Sep 28 16:11:53 2011 -0500
@@ -135,7 +135,7 @@
orgsend(self, data)
return _sendfile
-has_https = hasattr(urllib2, 'HTTPSHandler')
+has_https = util.safehasattr(urllib2, 'HTTPSHandler')
if has_https:
try:
_create_connection = socket.create_connection
@@ -192,8 +192,8 @@
# general transaction handler to support different ways to handle
# HTTPS proxying before and after Python 2.6.3.
def _generic_start_transaction(handler, h, req):
- if hasattr(req, '_tunnel_host') and req._tunnel_host:
- tunnel_host = req._tunnel_host
+ tunnel_host = getattr(req, '_tunnel_host', None)
+ if tunnel_host:
if tunnel_host[:7] not in ['http://', 'https:/']:
tunnel_host = 'https://' + tunnel_host
new_tunnel = True
--- a/mercurial/util.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/util.py Wed Sep 28 16:11:53 2011 -0500
@@ -19,6 +19,58 @@
import os, time, calendar, textwrap, signal
import imp, socket, urllib
+if os.name == 'nt':
+ import windows as platform
+else:
+ import posix as platform
+
+cachestat = platform.cachestat
+checkexec = platform.checkexec
+checklink = platform.checklink
+copymode = platform.copymode
+executablepath = platform.executablepath
+expandglobs = platform.expandglobs
+explainexit = platform.explainexit
+findexe = platform.findexe
+gethgcmd = platform.gethgcmd
+getuser = platform.getuser
+groupmembers = platform.groupmembers
+groupname = platform.groupname
+hidewindow = platform.hidewindow
+isexec = platform.isexec
+isowner = platform.isowner
+localpath = platform.localpath
+lookupreg = platform.lookupreg
+makedir = platform.makedir
+nlinks = platform.nlinks
+normpath = platform.normpath
+nulldev = platform.nulldev
+openhardlinks = platform.openhardlinks
+oslink = platform.oslink
+parsepatchoutput = platform.parsepatchoutput
+pconvert = platform.pconvert
+popen = platform.popen
+posixfile = platform.posixfile
+quotecommand = platform.quotecommand
+realpath = platform.realpath
+rename = platform.rename
+samedevice = platform.samedevice
+samefile = platform.samefile
+samestat = platform.samestat
+setbinary = platform.setbinary
+setflags = platform.setflags
+setsignalhandler = platform.setsignalhandler
+shellquote = platform.shellquote
+spawndetached = platform.spawndetached
+sshargs = platform.sshargs
+statfiles = platform.statfiles
+termwidth = platform.termwidth
+testpid = platform.testpid
+umask = platform.umask
+unlink = platform.unlink
+unlinkpath = platform.unlinkpath
+username = platform.username
+
# Python compatibility
def sha1(s):
@@ -307,8 +359,8 @@
The code supports py2exe (most common, Windows only) and tools/freeze
(portable, not much used).
"""
- return (hasattr(sys, "frozen") or # new py2exe
- hasattr(sys, "importers") or # old py2exe
+ return (safehasattr(sys, "frozen") or # new py2exe
+ safehasattr(sys, "importers") or # old py2exe
imp.is_frozen("__main__")) # tools/freeze
def hgexecutable():
@@ -318,10 +370,13 @@
"""
if _hgexecutable is None:
hg = os.environ.get('HG')
+ mainmod = sys.modules['__main__']
if hg:
_sethgexecutable(hg)
elif mainfrozen():
_sethgexecutable(sys.executable)
+ elif os.path.basename(getattr(mainmod, '__file__', '')) == 'hg':
+ _sethgexecutable(mainmod.__file__)
else:
exe = findexe('hg') or os.path.basename(sys.argv[0])
_sethgexecutable(exe)
@@ -394,18 +449,6 @@
return check
-def makedir(path, notindexed):
- os.mkdir(path)
-
-def unlinkpath(f):
- """unlink and remove the directory if it is empty"""
- os.unlink(f)
- # try removing directories that might now be empty
- try:
- os.removedirs(os.path.dirname(f))
- except OSError:
- pass
-
def copyfile(src, dest):
"copy a file, preserving mode and atime/mtime"
if os.path.islink(src):
@@ -491,22 +534,10 @@
return _("filename ends with '%s', which is not allowed "
"on Windows") % t
-def lookupreg(key, name=None, scope=None):
- return None
-
-def hidewindow():
- """Hide current shell window.
-
- Used to hide the window opened when starting asynchronous
- child process under Windows, unneeded on other systems.
- """
- pass
-
if os.name == 'nt':
checkosfilename = checkwinfilename
- from windows import *
else:
- from posix import *
+ checkosfilename = platform.checkosfilename
def makelock(info, pathname):
try:
@@ -690,16 +721,7 @@
# Temporary files are created with mode 0600, which is usually not
# what we want. If the original file already exists, just copy
# its mode. Otherwise, manually obey umask.
- try:
- st_mode = os.lstat(name).st_mode & 0777
- except OSError, inst:
- if inst.errno != errno.ENOENT:
- raise
- st_mode = createmode
- if st_mode is None:
- st_mode = ~umask
- st_mode &= 0666
- os.chmod(temp, st_mode)
+ copymode(name, temp, createmode)
if emptyok:
return temp
try:
@@ -726,11 +748,10 @@
'''writeable file object that atomically updates a file
All writes will go to a temporary copy of the original file. Call
- rename() when you are done writing, and atomictempfile will rename
- the temporary copy to the original name, making the changes visible.
-
- Unlike other file-like objects, close() discards your writes by
- simply deleting the temporary file.
+ close() when you are done writing, and atomictempfile will rename
+ the temporary copy to the original name, making the changes
+ visible. If the object is destroyed without being closed, all your
+ writes are discarded.
'''
def __init__(self, name, mode='w+b', createmode=None):
self.__name = name # permanent name
@@ -742,12 +763,12 @@
self.write = self._fp.write
self.fileno = self._fp.fileno
- def rename(self):
+ def close(self):
if not self._fp.closed:
self._fp.close()
rename(self._tempname, localpath(self.__name))
- def close(self):
+ def discard(self):
if not self._fp.closed:
try:
os.unlink(self._tempname)
@@ -756,24 +777,25 @@
self._fp.close()
def __del__(self):
- if hasattr(self, '_fp'): # constructor actually did something
- self.close()
+ if safehasattr(self, '_fp'): # constructor actually did something
+ self.discard()
def makedirs(name, mode=None):
"""recursive directory creation with parent mode inheritance"""
- parent = os.path.abspath(os.path.dirname(name))
try:
os.mkdir(name)
- if mode is not None:
- os.chmod(name, mode)
- return
except OSError, err:
if err.errno == errno.EEXIST:
return
- if not name or parent == name or err.errno != errno.ENOENT:
+ if err.errno != errno.ENOENT or not name:
+ raise
+ parent = os.path.dirname(os.path.abspath(name))
+ if parent == name:
raise
- makedirs(parent, mode)
- makedirs(name, mode)
+ makedirs(parent, mode)
+ os.mkdir(name)
+ if mode is not None:
+ os.chmod(name, mode)
def readfile(path):
fp = open(path, 'rb')
@@ -1303,8 +1325,9 @@
def handler(signum, frame):
terminated.add(os.wait())
prevhandler = None
- if hasattr(signal, 'SIGCHLD'):
- prevhandler = signal.signal(signal.SIGCHLD, handler)
+ SIGCHLD = getattr(signal, 'SIGCHLD', None)
+ if SIGCHLD is not None:
+ prevhandler = signal.signal(SIGCHLD, handler)
try:
pid = spawndetached(args)
while not condfn():
@@ -1648,8 +1671,10 @@
self.user, self.passwd = user, passwd
if not self.user:
return (s, None)
- # authinfo[1] is passed to urllib2 password manager, and its URIs
- # must not contain credentials.
+ # authinfo[1] is passed to urllib2 password manager, and its
+ # URIs must not contain credentials. The host is passed in the
+ # URIs list because Python < 2.4.3 uses only that to search for
+ # a password.
return (s, (None, (s, self.host),
self.user, self.passwd or ''))
--- a/mercurial/windows.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/windows.py Wed Sep 28 16:11:53 2011 -0500
@@ -9,6 +9,22 @@
import osutil
import errno, msvcrt, os, re, sys
+import win32
+executablepath = win32.executablepath
+getuser = win32.getuser
+hidewindow = win32.hidewindow
+lookupreg = win32.lookupreg
+makedir = win32.makedir
+nlinks = win32.nlinks
+oslink = win32.oslink
+samedevice = win32.samedevice
+samefile = win32.samefile
+setsignalhandler = win32.setsignalhandler
+spawndetached = win32.spawndetached
+termwidth = win32.termwidth
+testpid = win32.testpid
+unlink = win32.unlink
+
nulldev = 'NUL:'
umask = 002
@@ -90,6 +106,9 @@
def setflags(f, l, x):
pass
+def copymode(src, dst, mode=None):
+ pass
+
def checkexec(path):
return False
@@ -99,8 +118,9 @@
def setbinary(fd):
# When run without console, pipes may expose invalid
# fileno(), usually set to -1.
- if hasattr(fd, 'fileno') and fd.fileno() >= 0:
- msvcrt.setmode(fd.fileno(), os.O_BINARY)
+ fno = getattr(fd, 'fileno', None)
+ if fno is not None and fno() >= 0:
+ msvcrt.setmode(fno(), os.O_BINARY)
def pconvert(path):
return '/'.join(path.split(os.sep))
@@ -281,6 +301,14 @@
# Don't support groups on Windows for now
raise KeyError()
-from win32 import *
+def isexec(f):
+ return False
+
+class cachestat(object):
+ def __init__(self, path):
+ pass
+
+ def cacheable(self):
+ return False
expandglobs = True
--- a/mercurial/wireproto.py Tue Sep 20 15:21:27 2011 +0300
+++ b/mercurial/wireproto.py Wed Sep 28 16:11:53 2011 -0500
@@ -17,7 +17,7 @@
class future(object):
'''placeholder for a value to be set later'''
def set(self, value):
- if hasattr(self, 'value'):
+ if util.safehasattr(self, 'value'):
raise error.RepoError("future is already set")
self.value = value
@@ -58,8 +58,9 @@
req, rsp = [], []
for name, args, opts, resref in self.calls:
mtd = getattr(self.remote, name)
- if hasattr(mtd, 'batchable'):
- batchable = getattr(mtd, 'batchable')(mtd.im_self, *args, **opts)
+ batchablefn = getattr(mtd, 'batchable', None)
+ if batchablefn is not None:
+ batchable = batchablefn(mtd.im_self, *args, **opts)
encargsorres, encresref = batchable.next()
if encresref:
req.append((name, encargsorres,))
@@ -334,6 +335,10 @@
def __init__(self, res):
self.res = res
+class ooberror(object):
+ def __init__(self, message):
+ self.message = message
+
def dispatch(repo, proto, command):
func, spec = commands[command]
args = proto.getargs(spec)
@@ -375,6 +380,8 @@
result = func(repo, proto, *[data[k] for k in keys])
else:
result = func(repo, proto)
+ if isinstance(result, ooberror):
+ return result
res.append(escapearg(result))
return ';'.join(res)
--- a/setup.py Tue Sep 20 15:21:27 2011 +0300
+++ b/setup.py Wed Sep 28 16:11:53 2011 -0500
@@ -5,7 +5,7 @@
# 'python setup.py --help' for more options
import sys, platform
-if not hasattr(sys, 'version_info') or sys.version_info < (2, 4, 0, 'final'):
+if getattr(sys, 'version_info', (0, 0, 0)) < (2, 4, 0, 'final'):
raise SystemExit("Mercurial requires Python 2.4 or later.")
if sys.version_info[0] >= 3:
--- a/tests/hghave Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/hghave Wed Sep 28 16:11:53 2011 -0500
@@ -101,6 +101,16 @@
def has_fifo():
return hasattr(os, "mkfifo")
+def has_cacheable_fs():
+ from mercurial import util
+
+ fd, path = tempfile.mkstemp(prefix=tempprefix)
+ os.close(fd)
+ try:
+ return util.cachestat(path).cacheable()
+ finally:
+ os.remove(path)
+
def has_lsprof():
try:
import _lsprof
@@ -200,6 +210,7 @@
"baz": (has_baz, "GNU Arch baz client"),
"bzr": (has_bzr, "Canonical's Bazaar client"),
"bzr114": (has_bzr114, "Canonical's Bazaar client >= 1.14"),
+ "cacheable": (has_cacheable_fs, "cacheable filesystem"),
"cvs": (has_cvs, "cvs client/server"),
"darcs": (has_darcs, "darcs client"),
"docutils": (has_docutils, "Docutils text processing library"),
--- a/tests/run-tests.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/run-tests.py Wed Sep 28 16:11:53 2011 -0500
@@ -340,10 +340,7 @@
"""Terminate subprocess (with fallback for Python versions < 2.6)"""
vlog('# Terminating process %d' % proc.pid)
try:
- if hasattr(proc, 'terminate'):
- proc.terminate()
- else:
- os.kill(proc.pid, signal.SIGTERM)
+ getattr(proc, 'terminate', lambda : os.kill(proc.pid, signal.SIGTERM))()
except OSError:
pass
@@ -726,6 +723,7 @@
rename(testpath + ".err", testpath)
else:
rename(testpath + ".err", testpath + ".out")
+ result('p', test)
return
result('f', (test, msg))
--- a/tests/sitecustomize.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/sitecustomize.py Wed Sep 28 16:11:53 2011 -0500
@@ -1,6 +1,5 @@
try:
import coverage
- if hasattr(coverage, 'process_startup'):
- coverage.process_startup()
+ getattr(coverage, 'process_startup', lambda: None)()
except ImportError:
pass
--- a/tests/test-acl.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-acl.t Wed Sep 28 16:11:53 2011 -0500
@@ -121,7 +121,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 0 (undo push)
- working directory now based on revision 0
0:6675d58eff77
@@ -179,7 +178,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 0 (undo push)
- working directory now based on revision 0
0:6675d58eff77
@@ -247,7 +245,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 0 (undo push)
- working directory now based on revision 0
0:6675d58eff77
@@ -729,7 +726,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 0 (undo push)
- working directory now based on revision 0
0:6675d58eff77
@@ -1038,7 +1034,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 0 (undo push)
- working directory now based on revision 0
0:6675d58eff77
@@ -1114,7 +1109,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 0 (undo push)
- working directory now based on revision 0
0:6675d58eff77
@@ -1261,7 +1255,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 0 (undo push)
- working directory now based on revision 0
0:6675d58eff77
@@ -1456,7 +1449,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 2 (undo push)
- working directory now based on revision 2
2:fb35475503ef
@@ -1753,7 +1745,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 2 (undo push)
- working directory now based on revision 2
2:fb35475503ef
@@ -1838,7 +1829,6 @@
updating the branch cache
checking for updated bookmarks
repository tip rolled back to revision 2 (undo push)
- working directory now based on revision 2
2:fb35475503ef
Branch acl conflicting deny
--- a/tests/test-alias.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-alias.t Wed Sep 28 16:11:53 2011 -0500
@@ -251,7 +251,7 @@
$ hg --cwd .. count 'branch(default)'
2
$ hg echo --cwd ..
- --cwd ..
+
repo specific shell aliases
--- a/tests/test-atomictempfile.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-atomictempfile.py Wed Sep 28 16:11:53 2011 -0500
@@ -12,22 +12,21 @@
assert basename in glob.glob('.foo-*')
file.write('argh\n')
- file.rename()
+ file.close()
assert os.path.isfile('foo')
assert basename not in glob.glob('.foo-*')
print 'OK'
-# close() removes the temp file but does not make the write
-# permanent -- essentially discards your work (WTF?!)
-def test2_close():
+# discard() removes the temp file without making the write permanent
+def test2_discard():
if os.path.exists('foo'):
os.remove('foo')
file = atomictempfile('foo')
(dir, basename) = os.path.split(file._tempname)
file.write('yo\n')
- file.close()
+ file.discard()
assert not os.path.isfile('foo')
assert basename not in os.listdir('.')
@@ -45,5 +44,5 @@
if __name__ == '__main__':
test1_simple()
- test2_close()
+ test2_discard()
test3_oops()
--- a/tests/test-bisect.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-bisect.t Wed Sep 28 16:11:53 2011 -0500
@@ -377,7 +377,7 @@
date: Thu Jan 01 00:00:06 1970 +0000
summary: msg 6
- $ hg log -r "bisected(good)"
+ $ hg log -r "bisect(good)"
changeset: 0:b99c7b9c8e11
user: test
date: Thu Jan 01 00:00:00 1970 +0000
@@ -388,13 +388,13 @@
date: Thu Jan 01 00:00:05 1970 +0000
summary: msg 5
- $ hg log -r "bisected(bad)"
+ $ hg log -r "bisect(bad)"
changeset: 6:a3d5c6fdf0d3
user: test
date: Thu Jan 01 00:00:06 1970 +0000
summary: msg 6
- $ hg log -r "bisected(skip)"
+ $ hg log -r "bisect(skip)"
changeset: 1:5cd978ea5149
user: test
date: Thu Jan 01 00:00:01 1970 +0000
@@ -416,6 +416,15 @@
summary: msg 4
+test legacy bisected() keyword
+
+ $ hg log -r "bisected(bad)"
+ changeset: 6:a3d5c6fdf0d3
+ user: test
+ date: Thu Jan 01 00:00:06 1970 +0000
+ summary: msg 6
+
+
$ set +e
test invalid command
--- a/tests/test-bisect2.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-bisect2.t Wed Sep 28 16:11:53 2011 -0500
@@ -252,6 +252,25 @@
$ hg bisect -b 17 # -> update to rev 6
Testing changeset 6:a214d5d3811a (15 changesets remaining, ~3 tests)
0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 17:228c06deef46
+ $ hg log -q -r 'bisect(untested)'
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ $ hg log -q -r 'bisect(ignored)'
$ hg bisect -g # -> update to rev 13
Testing changeset 13:b0a32c86eb31 (9 changesets remaining, ~3 tests)
3 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -271,6 +290,58 @@
date: Thu Jan 01 00:00:09 1970 +0000
summary: 9
+ $ hg log -q -r 'bisect(range)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ 18:d42e18c7bc9b
+ $ hg log -q -r 'bisect(untested)'
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ $ hg log -q -r 'bisect(goods)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ $ hg log -q -r 'bisect(bads)'
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ 18:d42e18c7bc9b
complex bisect test 2 # first good rev is 13
@@ -282,9 +353,31 @@
$ hg bisect -s # -> update to rev 10
Testing changeset 10:429fcd26f52d (13 changesets remaining, ~3 tests)
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 6:a214d5d3811a
+ 18:d42e18c7bc9b
$ hg bisect -b # -> update to rev 12
Testing changeset 12:9f259202bbe7 (5 changesets remaining, ~2 tests)
3 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 18:d42e18c7bc9b
+ $ hg log -q -r 'bisect(untested)'
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
$ hg bisect -b # -> update to rev 13
Testing changeset 13:b0a32c86eb31 (3 changesets remaining, ~1 tests)
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -295,6 +388,21 @@
date: Thu Jan 01 00:00:13 1970 +0000
summary: 13
+ $ hg log -q -r 'bisect(range)'
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 18:d42e18c7bc9b
complex bisect test 3
@@ -306,6 +414,11 @@
$ hg bisect -b 16 # -> update to rev 6
Testing changeset 6:a214d5d3811a (13 changesets remaining, ~3 tests)
2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 16:609d82a7ebae
+ 17:228c06deef46
$ hg bisect -g # -> update to rev 13
Testing changeset 13:b0a32c86eb31 (8 changesets remaining, ~3 tests)
3 files updated, 0 files merged, 1 files removed, 0 files unresolved
@@ -315,12 +428,25 @@
$ hg bisect -s # -> update to rev 12
Testing changeset 12:9f259202bbe7 (8 changesets remaining, ~3 tests)
3 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 10:429fcd26f52d
+ 13:b0a32c86eb31
+ 16:609d82a7ebae
+ 17:228c06deef46
$ hg bisect -g # -> update to rev 9
Testing changeset 9:3c77083deb4a (5 changesets remaining, ~2 tests)
1 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ hg bisect -s # -> update to rev 15
Testing changeset 15:857b178a7cf3 (5 changesets remaining, ~2 tests)
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(ignored)'
$ hg bisect -b
Due to skipped revisions, the first bad revision could be any of:
changeset: 9:3c77083deb4a
@@ -347,6 +473,22 @@
date: Thu Jan 01 00:00:15 1970 +0000
summary: merge 10,13
+ $ hg log -q -r 'bisect(range)'
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ $ hg log -q -r 'bisect(ignored)'
complex bisect test 4
@@ -364,9 +506,40 @@
$ hg bisect -b # -> update to rev 15
Testing changeset 15:857b178a7cf3 (3 changesets remaining, ~1 tests)
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 17:228c06deef46
$ hg bisect -s # -> update to rev 16
Testing changeset 16:609d82a7ebae (3 changesets remaining, ~1 tests)
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 17:228c06deef46
$ hg bisect -s
Due to skipped revisions, the first good revision could be any of:
changeset: 15:857b178a7cf3
@@ -386,6 +559,33 @@
date: Thu Jan 01 00:00:17 1970 +0000
summary: 17
+ $ hg log -q -r 'bisect(range)'
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
test unrelated revs:
@@ -394,6 +594,15 @@
$ hg bisect -g 14
abort: starting revisions are not directly related
[255]
+ $ hg log -q -r 'bisect(range)'
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 7:50c76098bbf2
+ 14:faa450606157
$ hg bisect --reset
end at merge: 17 bad, 11 good (but 9 is first bad)
@@ -403,6 +612,14 @@
$ hg bisect -g 11
Testing changeset 13:b0a32c86eb31 (5 changesets remaining, ~2 tests)
3 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(ignored)'
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 9:3c77083deb4a
+ 10:429fcd26f52d
$ hg bisect -g
Testing changeset 15:857b178a7cf3 (3 changesets remaining, ~1 tests)
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -418,12 +635,69 @@
Not all ancestors of this changeset have been checked.
Use bisect --extend to continue the bisection from
the common ancestor, dab8161ac8fc.
+ $ hg log -q -r 'bisect(range)'
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 8:dab8161ac8fc
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ 18:d42e18c7bc9b
+ $ hg log -q -r 'bisect(untested)'
+ $ hg log -q -r 'bisect(ignored)'
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 9:3c77083deb4a
+ 10:429fcd26f52d
$ hg bisect --extend
Extending search to changeset 8:dab8161ac8fc
2 files updated, 0 files merged, 2 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(untested)'
+ $ hg log -q -r 'bisect(ignored)'
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ 9:3c77083deb4a
+ 10:429fcd26f52d
$ hg bisect -g # dab8161ac8fc
Testing changeset 9:3c77083deb4a (3 changesets remaining, ~1 tests)
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(untested)'
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ $ hg log -q -r 'bisect(ignored)'
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ $ hg log -q -r 'bisect(goods)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 8:dab8161ac8fc
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ $ hg log -q -r 'bisect(bads)'
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ 18:d42e18c7bc9b
$ hg bisect -b
The first bad revision is:
changeset: 9:3c77083deb4a
@@ -431,3 +705,91 @@
date: Thu Jan 01 00:00:09 1970 +0000
summary: 9
+ $ hg log -q -r 'bisect(range)'
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 8:dab8161ac8fc
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ 18:d42e18c7bc9b
+ $ hg log -q -r 'bisect(untested)'
+ $ hg log -q -r 'bisect(ignored)'
+ 2:051e12f87bf1
+ 3:0950834f0a9c
+ 4:5c668c22234f
+ 5:385a529b6670
+ 6:a214d5d3811a
+ $ hg log -q -r 'bisect(goods)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 8:dab8161ac8fc
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ $ hg log -q -r 'bisect(bads)'
+ 9:3c77083deb4a
+ 10:429fcd26f52d
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ 18:d42e18c7bc9b
+
+user adds irrelevant but consistent information (here: -g 2) to bisect state
+
+ $ hg bisect -r
+ $ hg bisect -b 13
+ $ hg bisect -g 8
+ Testing changeset 11:82ca6f06eccd (3 changesets remaining, ~1 tests)
+ 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(untested)'
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ $ hg bisect -g 2
+ Testing changeset 11:82ca6f06eccd (3 changesets remaining, ~1 tests)
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ $ hg log -q -r 'bisect(untested)'
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ $ hg bisect -b
+ The first bad revision is:
+ changeset: 11:82ca6f06eccd
+ parent: 8:dab8161ac8fc
+ user: test
+ date: Thu Jan 01 00:00:11 1970 +0000
+ summary: 11
+
+ $ hg log -q -r 'bisect(range)'
+ 8:dab8161ac8fc
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ $ hg log -q -r 'bisect(pruned)'
+ 0:33b1f9bc8bc5
+ 1:4ca5088da217
+ 2:051e12f87bf1
+ 8:dab8161ac8fc
+ 11:82ca6f06eccd
+ 12:9f259202bbe7
+ 13:b0a32c86eb31
+ 14:faa450606157
+ 15:857b178a7cf3
+ 16:609d82a7ebae
+ 17:228c06deef46
+ 18:d42e18c7bc9b
+ $ hg log -q -r 'bisect(untested)'
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-bisect3.t Wed Sep 28 16:11:53 2011 -0500
@@ -0,0 +1,232 @@
+# Here we create a simple DAG which has just enough of the required
+# topology to test all the bisection status labels:
+#
+# 13--14
+# /
+# 0--1--2--3---------9--10--11--12
+# \ /
+# 4--5--6--7--8
+
+
+ $ hg init
+
+ $ echo '0' >a
+ $ hg add a
+ $ hg ci -u test -d '0 0' -m '0'
+ $ echo '1' >a
+ $ hg ci -u test -d '0 1' -m '1'
+
+branch 2-3
+
+ $ echo '2' >b
+ $ hg add b
+ $ hg ci -u test -d '0 2' -m '2'
+ $ echo '3' >b
+ $ hg ci -u test -d '0 3' -m '3'
+
+branch 4-8
+
+ $ hg up -r 1
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo '4' >c
+ $ hg add c
+ $ hg ci -u test -d '0 4' -m '4'
+ created new head
+ $ echo '5' >c
+ $ hg ci -u test -d '0 5' -m '5'
+ $ echo '6' >c
+ $ hg ci -u test -d '0 6' -m '6'
+ $ echo '7' >c
+ $ hg ci -u test -d '0 7' -m '7'
+ $ echo '8' >c
+ $ hg ci -u test -d '0 8' -m '8'
+
+merge
+
+ $ hg merge -r 3
+ 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
+ (branch merge, don't forget to commit)
+ $ hg ci -u test -d '0 9' -m '9=8+3'
+
+ $ echo '10' >a
+ $ hg ci -u test -d '0 10' -m '10'
+ $ echo '11' >a
+ $ hg ci -u test -d '0 11' -m '11'
+ $ echo '12' >a
+ $ hg ci -u test -d '0 12' -m '12'
+
+unrelated branch
+
+ $ hg up -r 3
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo '13' >d
+ $ hg add d
+ $ hg ci -u test -d '0 13' -m '13'
+ created new head
+ $ echo '14' >d
+ $ hg ci -u test -d '0 14' -m '14'
+
+mark changesets
+
+ $ hg bisect --reset
+ $ hg bisect --good 4
+ $ hg bisect --good 6
+ $ hg bisect --bad 12
+ Testing changeset 9:8bcbdb072033 (6 changesets remaining, ~2 tests)
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg bisect --bad 10
+ Testing changeset 8:3cd112f87d77 (4 changesets remaining, ~2 tests)
+ 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg bisect --skip 7
+ Testing changeset 8:3cd112f87d77 (4 changesets remaining, ~2 tests)
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+test template
+
+ $ hg log --template '{rev}:{node|short} {bisect}\n'
+ 14:cecd84203acc
+ 13:86f7c8cdb6df
+ 12:a76089b5f47c bad
+ 11:5c3eb122d29c bad (implicit)
+ 10:b097cef2be03 bad
+ 9:8bcbdb072033 untested
+ 8:3cd112f87d77 untested
+ 7:577e237a73bd skipped
+ 6:e597fa2707c5 good
+ 5:b9cea37a76bc good (implicit)
+ 4:da6b357259d7 good
+ 3:e7f031aee8ca ignored
+ 2:b1ad1b6bcc5c ignored
+ 1:37f42ae8b45e good (implicit)
+ 0:b4e73ffab476 good (implicit)
+ $ hg log --template '{bisect|shortbisect} {rev}:{node|short}\n'
+ 14:cecd84203acc
+ 13:86f7c8cdb6df
+ B 12:a76089b5f47c
+ B 11:5c3eb122d29c
+ B 10:b097cef2be03
+ U 9:8bcbdb072033
+ U 8:3cd112f87d77
+ S 7:577e237a73bd
+ G 6:e597fa2707c5
+ G 5:b9cea37a76bc
+ G 4:da6b357259d7
+ I 3:e7f031aee8ca
+ I 2:b1ad1b6bcc5c
+ G 1:37f42ae8b45e
+ G 0:b4e73ffab476
+
+test style
+
+ $ hg log --style bisect
+ changeset: 14:cecd84203acc
+ bisect:
+ tag: tip
+ user: test
+ date: Wed Dec 31 23:59:46 1969 -0000
+ summary: 14
+
+ changeset: 13:86f7c8cdb6df
+ bisect:
+ parent: 3:e7f031aee8ca
+ user: test
+ date: Wed Dec 31 23:59:47 1969 -0000
+ summary: 13
+
+ changeset: 12:a76089b5f47c
+ bisect: bad
+ user: test
+ date: Wed Dec 31 23:59:48 1969 -0000
+ summary: 12
+
+ changeset: 11:5c3eb122d29c
+ bisect: bad (implicit)
+ user: test
+ date: Wed Dec 31 23:59:49 1969 -0000
+ summary: 11
+
+ changeset: 10:b097cef2be03
+ bisect: bad
+ user: test
+ date: Wed Dec 31 23:59:50 1969 -0000
+ summary: 10
+
+ changeset: 9:8bcbdb072033
+ bisect: untested
+ parent: 8:3cd112f87d77
+ parent: 3:e7f031aee8ca
+ user: test
+ date: Wed Dec 31 23:59:51 1969 -0000
+ summary: 9=8+3
+
+ changeset: 8:3cd112f87d77
+ bisect: untested
+ user: test
+ date: Wed Dec 31 23:59:52 1969 -0000
+ summary: 8
+
+ changeset: 7:577e237a73bd
+ bisect: skipped
+ user: test
+ date: Wed Dec 31 23:59:53 1969 -0000
+ summary: 7
+
+ changeset: 6:e597fa2707c5
+ bisect: good
+ user: test
+ date: Wed Dec 31 23:59:54 1969 -0000
+ summary: 6
+
+ changeset: 5:b9cea37a76bc
+ bisect: good (implicit)
+ user: test
+ date: Wed Dec 31 23:59:55 1969 -0000
+ summary: 5
+
+ changeset: 4:da6b357259d7
+ bisect: good
+ parent: 1:37f42ae8b45e
+ user: test
+ date: Wed Dec 31 23:59:56 1969 -0000
+ summary: 4
+
+ changeset: 3:e7f031aee8ca
+ bisect: ignored
+ user: test
+ date: Wed Dec 31 23:59:57 1969 -0000
+ summary: 3
+
+ changeset: 2:b1ad1b6bcc5c
+ bisect: ignored
+ user: test
+ date: Wed Dec 31 23:59:58 1969 -0000
+ summary: 2
+
+ changeset: 1:37f42ae8b45e
+ bisect: good (implicit)
+ user: test
+ date: Wed Dec 31 23:59:59 1969 -0000
+ summary: 1
+
+ changeset: 0:b4e73ffab476
+ bisect: good (implicit)
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: 0
+
+ $ hg log --quiet --style bisect
+ 14:cecd84203acc
+ 13:86f7c8cdb6df
+ B 12:a76089b5f47c
+ B 11:5c3eb122d29c
+ B 10:b097cef2be03
+ U 9:8bcbdb072033
+ U 8:3cd112f87d77
+ S 7:577e237a73bd
+ G 6:e597fa2707c5
+ G 5:b9cea37a76bc
+ G 4:da6b357259d7
+ I 3:e7f031aee8ca
+ I 2:b1ad1b6bcc5c
+ G 1:37f42ae8b45e
+ G 0:b4e73ffab476
--- a/tests/test-bookmarks.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-bookmarks.t Wed Sep 28 16:11:53 2011 -0500
@@ -239,9 +239,10 @@
test summary
$ hg summary
- parent: 2:db815d6d32e6 tip Y Z x y
+ parent: 2:db815d6d32e6 tip
2
branch: default
+ bookmarks: *Z Y x y
commit: (clean)
update: 1 new changesets, 2 branch heads (merge)
@@ -342,3 +343,19 @@
* Z 3:125c9a1d6df6
x y 2:db815d6d32e6
+test wrongly formated bookmark
+
+ $ echo '' >> .hg/bookmarks
+ $ hg bookmarks
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ * Z 3:125c9a1d6df6
+ x y 2:db815d6d32e6
+ $ echo "Ican'thasformatedlines" >> .hg/bookmarks
+ $ hg bookmarks
+ malformed line in .hg/bookmarks: "Ican'thasformatedlines"
+ X2 1:925d80f479bb
+ Y 2:db815d6d32e6
+ * Z 3:125c9a1d6df6
+ x y 2:db815d6d32e6
+
--- a/tests/test-bundle-r.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-bundle-r.t Wed Sep 28 16:11:53 2011 -0500
@@ -154,7 +154,6 @@
4 files, 9 changesets, 7 total revisions
$ hg rollback
repository tip rolled back to revision 4 (undo pull)
- working directory now based on revision -1
$ cd ..
should fail
@@ -232,7 +231,6 @@
4 files, 9 changesets, 7 total revisions
$ hg rollback
repository tip rolled back to revision 2 (undo unbundle)
- working directory now based on revision 2
revision 2
@@ -257,7 +255,6 @@
2 files, 5 changesets, 5 total revisions
$ hg rollback
repository tip rolled back to revision 2 (undo unbundle)
- working directory now based on revision 2
$ hg unbundle ../test-bundle-branch2.hg
adding changesets
adding manifests
@@ -277,7 +274,6 @@
3 files, 7 changesets, 6 total revisions
$ hg rollback
repository tip rolled back to revision 2 (undo unbundle)
- working directory now based on revision 2
$ hg unbundle ../test-bundle-cset-7.hg
adding changesets
adding manifests
--- a/tests/test-bundle.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-bundle.t Wed Sep 28 16:11:53 2011 -0500
@@ -90,7 +90,6 @@
$ hg -R empty rollback
repository tip rolled back to revision -1 (undo pull)
- working directory now based on revision -1
Pull full.hg into empty again (using --cwd)
@@ -121,7 +120,6 @@
$ hg -R empty rollback
repository tip rolled back to revision -1 (undo pull)
- working directory now based on revision -1
Pull full.hg into empty again (using -R)
@@ -219,7 +217,6 @@
$ hg rollback
repository tip rolled back to revision -1 (undo pull)
- working directory now based on revision -1
$ cd ..
Log -R bundle:empty+full.hg
--- a/tests/test-check-pyflakes.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-check-pyflakes.t Wed Sep 28 16:11:53 2011 -0500
@@ -7,8 +7,5 @@
mercurial/commands.py:*: 'mpatch' imported but unused (glob)
mercurial/commands.py:*: 'osutil' imported but unused (glob)
hgext/inotify/linux/__init__.py:*: 'from _inotify import *' used; unable to detect undefined names (glob)
- mercurial/util.py:*: 'from posix import *' used; unable to detect undefined names (glob)
- mercurial/windows.py:*: 'from win32 import *' used; unable to detect undefined names (glob)
- mercurial/util.py:*: 'from windows import *' used; unable to detect undefined names (glob)
--- a/tests/test-commandserver.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-commandserver.py Wed Sep 28 16:11:53 2011 -0500
@@ -154,6 +154,30 @@
'hooks.pre-identify=python:test-commandserver.hook', 'id'],
input=cStringIO.StringIO('some input'))
+def outsidechanges(server):
+ readchannel(server)
+ os.system('echo a >> a && hg ci -Am2')
+ runcommand(server, ['tip'])
+
+def bookmarks(server):
+ readchannel(server)
+ runcommand(server, ['bookmarks'])
+
+ # changes .hg/bookmarks
+ os.system('hg bookmark -i bm1')
+ os.system('hg bookmark -i bm2')
+ runcommand(server, ['bookmarks'])
+
+ # changes .hg/bookmarks.current
+ os.system('hg upd bm1 -q')
+ runcommand(server, ['bookmarks'])
+
+def tagscache(server):
+ readchannel(server)
+ runcommand(server, ['id', '-t', '-r', '0'])
+ os.system('hg tag -r 0 foo')
+ runcommand(server, ['id', '-t', '-r', '0'])
+
if __name__ == '__main__':
os.system('hg init')
@@ -169,3 +193,6 @@
hgrc.close()
check(localhgrc)
check(hookoutput)
+ check(outsidechanges)
+ check(bookmarks)
+ check(tagscache)
--- a/tests/test-commandserver.py.out Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-commandserver.py.out Wed Sep 28 16:11:53 2011 -0500
@@ -52,3 +52,16 @@
hook talking
now try to read something: 'some input'
eff892de26ec tip
+changeset: 1:d3a0a68be6de
+tag: tip
+user: test
+date: Thu Jan 01 00:00:00 1970 +0000
+summary: 2
+
+no bookmarks set
+ bm1 1:d3a0a68be6de
+ bm2 1:d3a0a68be6de
+ * bm1 1:d3a0a68be6de
+ bm2 1:d3a0a68be6de
+
+foo
--- a/tests/test-convert-authormap.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-convert-authormap.t Wed Sep 28 16:11:53 2011 -0500
@@ -27,7 +27,7 @@
sorting...
converting...
0 foo
- Writing author map file new/.hg/authormap
+ Writing author map file $TESTTMP/new/.hg/authormap
$ cat new/.hg/authormap
user name=Long User Name
$ hg -Rnew log
@@ -44,7 +44,7 @@
$ hg init new
$ mv authormap.txt new/.hg/authormap
$ hg convert orig new
- Ignoring bad line in author map file new/.hg/authormap: this line is ignored
+ Ignoring bad line in author map file $TESTTMP/new/.hg/authormap: this line is ignored
scanning source...
sorting...
converting...
--- a/tests/test-convert-cvs.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-convert-cvs.t Wed Sep 28 16:11:53 2011 -0500
@@ -112,7 +112,6 @@
1 import
filtering out empty revision
repository tip rolled back to revision 0 (undo commit)
- working directory now based on revision -1
0 ci0
updating tags
$ hgcat b/c
--- a/tests/test-convert-svn-move.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-convert-svn-move.t Wed Sep 28 16:11:53 2011 -0500
@@ -167,6 +167,7 @@
> [progress]
> assume-tty = 1
> delay = 0
+ > changedelay = 0
> format = topic bar number
> refresh = 0
> width = 60
--- a/tests/test-convert.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-convert.t Wed Sep 28 16:11:53 2011 -0500
@@ -249,16 +249,16 @@
options:
- -s --source-type TYPE source repository type
- -d --dest-type TYPE destination repository type
- -r --rev REV import up to target revision REV
- -A --authormap FILE remap usernames using this file
- --filemap FILE remap file names using contents of file
- --splicemap FILE splice synthesized history into place
- --branchmap FILE change branch names while converting
- --branchsort try to sort changesets by branches
- --datesort try to sort changesets by date
- --sourcesort preserve source changesets order
+ -s --source-type TYPE source repository type
+ -d --dest-type TYPE destination repository type
+ -r --rev REV import up to target revision REV
+ -A --authormap FILE remap usernames using this file
+ --filemap FILE remap file names using contents of file
+ --splicemap FILE splice synthesized history into place
+ --branchmap FILE change branch names while converting
+ --branchsort try to sort changesets by branches
+ --datesort try to sort changesets by date
+ --sourcesort preserve source changesets order
use "hg -v help convert" to show global options
$ hg init a
--- a/tests/test-debugcomplete.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-debugcomplete.t Wed Sep 28 16:11:53 2011 -0500
@@ -196,7 +196,7 @@
forget: include, exclude
init: ssh, remotecmd, insecure
log: follow, follow-first, date, copies, keyword, rev, removed, only-merges, user, only-branch, branch, prune, hidden, patch, git, limit, no-merges, stat, style, template, include, exclude
- merge: force, tool, rev, preview
+ merge: force, rev, preview, tool
pull: update, force, rev, bookmark, branch, ssh, remotecmd, insecure
push: force, rev, bookmark, branch, new-branch, ssh, remotecmd, insecure
remove: after, force, include, exclude
@@ -206,7 +206,7 @@
update: clean, check, date, rev
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
+ backout: merge, parent, rev, tool, include, exclude, message, logfile, date, user
bisect: reset, good, bad, skip, extend, command, noupdate
bookmarks: force, rev, delete, rename, inactive
branch: force, clean
@@ -255,7 +255,7 @@
paths:
recover:
rename: after, force, include, exclude, dry-run
- resolve: all, list, mark, unmark, tool, no-status, include, exclude
+ resolve: all, list, mark, unmark, no-status, tool, include, exclude
revert: all, date, rev, no-backup, include, exclude, dry-run
rollback: dry-run
root:
--- a/tests/test-dispatch.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-dispatch.t Wed Sep 28 16:11:53 2011 -0500
@@ -25,11 +25,11 @@
options:
- -o --output FORMAT print output to file with formatted name
- -r --rev REV print the given revision
- --decode apply any matching decode filter
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
+ -o --output FORMAT print output to file with formatted name
+ -r --rev REV print the given revision
+ --decode apply any matching decode filter
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
[+] marked option can be specified multiple times
--- a/tests/test-doctest.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-doctest.py Wed Sep 28 16:11:53 2011 -0500
@@ -33,3 +33,6 @@
import hgext.convert.cvsps
doctest.testmod(hgext.convert.cvsps)
+
+import mercurial.revset
+doctest.testmod(mercurial.revset)
--- a/tests/test-duplicateoptions.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-duplicateoptions.py Wed Sep 28 16:11:53 2011 -0500
@@ -19,9 +19,15 @@
u = ui.ui()
extensions.loadall(u)
+globalshort = set()
+globallong = set()
+for option in commands.globalopts:
+ option[0] and globalshort.add(option[0])
+ option[1] and globallong.add(option[1])
+
for cmd, entry in commands.table.iteritems():
- seenshort = set()
- seenlong = set()
+ seenshort = globalshort.copy()
+ seenlong = globallong.copy()
for option in entry[1]:
if (option[0] and option[0] in seenshort) or \
(option[1] and option[1] in seenlong):
--- a/tests/test-encoding-align.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-encoding-align.t Wed Sep 28 16:11:53 2011 -0500
@@ -52,12 +52,12 @@
options:
- -s --opt1 \xe7\x9f\xad\xe5\x90\x8d short width \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d (esc)
- -m --opt2 MIDDLE_ middle width MIDDLE_ MIDDLE_ MIDDLE_ MIDDLE_ MIDDLE_
- MIDDLE_ MIDDLE_ MIDDLE_
- -l --opt3 \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d long width \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
- \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
- \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
+ -s --opt1 \xe7\x9f\xad\xe5\x90\x8d short width \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d \xe7\x9f\xad\xe5\x90\x8d (esc)
+ -m --opt2 MIDDLE_ middle width MIDDLE_ MIDDLE_ MIDDLE_ MIDDLE_ MIDDLE_
+ MIDDLE_ MIDDLE_ MIDDLE_
+ -l --opt3 \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d long width \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
+ \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
+ \xe9\x95\xb7\xe3\x81\x84\xe9\x95\xb7\xe3\x81\x84\xe5\x90\x8d\xe5\x89\x8d (esc)
use "hg -v help showoptlist" to show global options
--- a/tests/test-eol-hook.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-eol-hook.t Wed Sep 28 16:11:53 2011 -0500
@@ -161,7 +161,6 @@
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
--- a/tests/test-eol.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-eol.t Wed Sep 28 16:11:53 2011 -0500
@@ -441,3 +441,82 @@
warning: ignoring .hgeol file due to parse error at .hgeol:1: bad
$ hg status
? .hgeol.orig
+
+Test eol.only-consistent can be specified in .hgeol
+
+ $ cd $TESTTMP
+ $ hg init only-consistent
+ $ cd only-consistent
+ $ printf "first\nsecond\r\n" > a.txt
+ $ hg add a.txt
+ $ cat > .hgeol << EOF
+ > [eol]
+ > only-consistent = True
+ > EOF
+ $ hg commit -m 'inconsistent'
+ abort: inconsistent newline style in a.txt
+
+ [255]
+ $ cat > .hgeol << EOF
+ > [eol]
+ > only-consistent = False
+ > EOF
+ $ hg commit -m 'consistent'
+
+
+Test trailing newline
+
+ $ cat >> $HGRCPATH <<EOF
+ > [extensions]
+ > eol=
+ > EOF
+
+setup repository
+
+ $ cd $TESTTMP
+ $ hg init trailing
+ $ cd trailing
+ $ cat > .hgeol <<EOF
+ > [patterns]
+ > **.txt = native
+ > [eol]
+ > fix-trailing-newline = False
+ > EOF
+
+add text without trailing newline
+
+ $ printf "first\nsecond" > a.txt
+ $ hg commit --addremove -m 'checking in'
+ adding .hgeol
+ adding a.txt
+ $ rm a.txt
+ $ hg update -C -q
+ $ cat a.txt
+ first
+ second (no-eol)
+
+ $ cat > .hgeol <<EOF
+ > [patterns]
+ > **.txt = native
+ > [eol]
+ > fix-trailing-newline = True
+ > EOF
+ $ printf "third\nfourth" > a.txt
+ $ hg commit -m 'checking in with newline fix'
+ $ rm a.txt
+ $ hg update -C -q
+ $ cat a.txt
+ third
+ fourth
+
+append a line without trailing newline
+
+ $ printf "fifth" >> a.txt
+ $ hg commit -m 'adding another line line'
+ $ rm a.txt
+ $ hg update -C -q
+ $ cat a.txt
+ third
+ fourth
+ fifth
+
--- a/tests/test-export.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-export.t Wed Sep 28 16:11:53 2011 -0500
@@ -7,7 +7,7 @@
> hg ci -m "foo-$i"
> done
- $ for out in "%nof%N" "%%%H" "%b-%R" "%h" "%r"; do
+ $ for out in "%nof%N" "%%%H" "%b-%R" "%h" "%r" "%m"; do
> echo
> echo "# foo-$out.patch"
> hg export -v -o "foo-$out.patch" 2:tip
@@ -77,6 +77,19 @@
foo-09.patch
foo-10.patch
foo-11.patch
+
+ # foo-%m.patch
+ exporting patches:
+ foo-foo_2.patch
+ foo-foo_3.patch
+ foo-foo_4.patch
+ foo-foo_5.patch
+ foo-foo_6.patch
+ foo-foo_7.patch
+ foo-foo_8.patch
+ foo-foo_9.patch
+ foo-foo_10.patch
+ foo-foo_11.patch
Exporting 4 changesets to a file:
@@ -108,3 +121,11 @@
foo-9
+foo-10
+Checking if only alphanumeric characters are used in the file name (%m option):
+
+ $ echo "line" >> foo
+ $ hg commit -m " !\"#$%&(,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\`abcdefghijklmnopqrstuvwxyz{|}~"
+ $ hg export -v -o %m.patch tip
+ exporting patch:
+ ____________0123456789_______ABCDEFGHIJKLMNOPQRSTUVWXYZ______abcdefghijklmnopqrstuvwxyz____.patch
+
--- a/tests/test-extdiff.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-extdiff.t Wed Sep 28 16:11:53 2011 -0500
@@ -39,11 +39,11 @@
options:
- -o --option OPT [+] pass option to comparison program
- -r --rev REV [+] revision
- -c --change REV change made by revision
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
+ -o --option OPT [+] pass option to comparison program
+ -r --rev REV [+] revision
+ -c --change REV change made by revision
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
[+] marked option can be specified multiple times
--- a/tests/test-extension.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-extension.t Wed Sep 28 16:11:53 2011 -0500
@@ -182,23 +182,24 @@
yet another foo command
global options:
- -R --repository REPO repository root directory or name of overlay bundle
- file
- --cwd DIR change working directory
- -y --noninteractive do not prompt, automatically pick the first choice
- for all prompts
- -q --quiet suppress output
- -v --verbose enable additional output
- --config CONFIG [+] set/override config option (use 'section.name=value')
- --debug enable debugging output
- --debugger start debugger
- --encoding ENCODE set the charset encoding (default: ascii)
- --encodingmode MODE set the charset encoding mode (default: strict)
- --traceback always print a traceback on exception
- --time time how long the command takes
- --profile print command execution profile
- --version output version information and exit
- -h --help display help and exit
+
+ -R --repository REPO repository root directory or name of overlay bundle
+ file
+ --cwd DIR change working directory
+ -y --noninteractive do not prompt, automatically pick the first choice for
+ all prompts
+ -q --quiet suppress output
+ -v --verbose enable additional output
+ --config CONFIG [+] set/override config option (use 'section.name=value')
+ --debug enable debugging output
+ --debugger start debugger
+ --encoding ENCODE set the charset encoding (default: ascii)
+ --encodingmode MODE set the charset encoding mode (default: strict)
+ --traceback always print a traceback on exception
+ --time time how long the command takes
+ --profile print command execution profile
+ --version output version information and exit
+ -h --help display help and exit
[+] marked option can be specified multiple times
@@ -213,23 +214,24 @@
yet another foo command
global options:
- -R --repository REPO repository root directory or name of overlay bundle
- file
- --cwd DIR change working directory
- -y --noninteractive do not prompt, automatically pick the first choice
- for all prompts
- -q --quiet suppress output
- -v --verbose enable additional output
- --config CONFIG [+] set/override config option (use 'section.name=value')
- --debug enable debugging output
- --debugger start debugger
- --encoding ENCODE set the charset encoding (default: ascii)
- --encodingmode MODE set the charset encoding mode (default: strict)
- --traceback always print a traceback on exception
- --time time how long the command takes
- --profile print command execution profile
- --version output version information and exit
- -h --help display help and exit
+
+ -R --repository REPO repository root directory or name of overlay bundle
+ file
+ --cwd DIR change working directory
+ -y --noninteractive do not prompt, automatically pick the first choice for
+ all prompts
+ -q --quiet suppress output
+ -v --verbose enable additional output
+ --config CONFIG [+] set/override config option (use 'section.name=value')
+ --debug enable debugging output
+ --debugger start debugger
+ --encoding ENCODE set the charset encoding (default: ascii)
+ --encodingmode MODE set the charset encoding mode (default: strict)
+ --traceback always print a traceback on exception
+ --time time how long the command takes
+ --profile print command execution profile
+ --version output version information and exit
+ -h --help display help and exit
[+] marked option can be specified multiple times
$ echo 'debugextension = !' >> $HGRCPATH
@@ -260,12 +262,12 @@
options:
- -p --program CMD comparison program to run
- -o --option OPT [+] pass option to comparison program
- -r --rev REV [+] revision
- -c --change REV change made by revision
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
+ -p --program CMD comparison program to run
+ -o --option OPT [+] pass option to comparison program
+ -r --rev REV [+] revision
+ -c --change REV change made by revision
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
[+] marked option can be specified multiple times
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-filecache.py Wed Sep 28 16:11:53 2011 -0500
@@ -0,0 +1,94 @@
+import sys, os, subprocess
+
+if subprocess.call(['%s/hghave' % os.environ['TESTDIR'], 'cacheable']):
+ sys.exit(80)
+
+from mercurial import util, scmutil, extensions
+
+filecache = scmutil.filecache
+
+class fakerepo(object):
+ def __init__(self):
+ self._filecache = {}
+
+ def join(self, p):
+ return p
+
+ def sjoin(self, p):
+ return p
+
+ @filecache('x')
+ def cached(self):
+ print 'creating'
+
+ def invalidate(self):
+ for k in self._filecache:
+ try:
+ delattr(self, k)
+ except AttributeError:
+ pass
+
+def basic(repo):
+ # file doesn't exist, calls function
+ repo.cached
+
+ repo.invalidate()
+ # file still doesn't exist, uses cache
+ repo.cached
+
+ # create empty file
+ f = open('x', 'w')
+ f.close()
+ repo.invalidate()
+ # should recreate the object
+ repo.cached
+
+ f = open('x', 'w')
+ f.write('a')
+ f.close()
+ repo.invalidate()
+ # should recreate the object
+ repo.cached
+
+ repo.invalidate()
+ # stats file again, nothing changed, reuses object
+ repo.cached
+
+ # atomic replace file, size doesn't change
+ # hopefully st_mtime doesn't change as well so this doesn't use the cache
+ # because of inode change
+ f = scmutil.opener('.')('x', 'w', atomictemp=True)
+ f.write('b')
+ f.close()
+
+ repo.invalidate()
+ repo.cached
+
+def fakeuncacheable():
+ def wrapcacheable(orig, *args, **kwargs):
+ return False
+
+ def wrapinit(orig, *args, **kwargs):
+ pass
+
+ originit = extensions.wrapfunction(util.cachestat, '__init__', wrapinit)
+ origcacheable = extensions.wrapfunction(util.cachestat, 'cacheable',
+ wrapcacheable)
+
+ try:
+ os.remove('x')
+ except:
+ pass
+
+ basic(fakerepo())
+
+ util.cachestat.cacheable = origcacheable
+ util.cachestat.__init__ = originit
+
+print 'basic:'
+print
+basic(fakerepo())
+print
+print 'fakeuncacheable:'
+print
+fakeuncacheable()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-filecache.py.out Wed Sep 28 16:11:53 2011 -0500
@@ -0,0 +1,15 @@
+basic:
+
+creating
+creating
+creating
+creating
+
+fakeuncacheable:
+
+creating
+creating
+creating
+creating
+creating
+creating
--- a/tests/test-help.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-help.t Wed Sep 28 16:11:53 2011 -0500
@@ -199,12 +199,7 @@
Test short command list with verbose option
$ hg -v help shortlist
- Mercurial Distributed SCM (version *) (glob)
- (see http://mercurial.selenic.com for more information)
-
- Copyright (C) 2005-2011 Matt Mackall and others
- This is free software; see the source for copying conditions. There is NO
- warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ Mercurial Distributed SCM
basic commands:
@@ -244,23 +239,24 @@
update working directory (or switch revisions)
global options:
- -R --repository REPO repository root directory or name of overlay bundle
- file
- --cwd DIR change working directory
- -y --noninteractive do not prompt, automatically pick the first choice
- for all prompts
- -q --quiet suppress output
- -v --verbose enable additional output
- --config CONFIG [+] set/override config option (use 'section.name=value')
- --debug enable debugging output
- --debugger start debugger
- --encoding ENCODE set the charset encoding (default: ascii)
- --encodingmode MODE set the charset encoding mode (default: strict)
- --traceback always print a traceback on exception
- --time time how long the command takes
- --profile print command execution profile
- --version output version information and exit
- -h --help display help and exit
+
+ -R --repository REPO repository root directory or name of overlay bundle
+ file
+ --cwd DIR change working directory
+ -y --noninteractive do not prompt, automatically pick the first choice for
+ all prompts
+ -q --quiet suppress output
+ -v --verbose enable additional output
+ --config CONFIG [+] set/override config option (use 'section.name=value')
+ --debug enable debugging output
+ --debugger start debugger
+ --encoding ENCODE set the charset encoding (default: ascii)
+ --encodingmode MODE set the charset encoding mode (default: strict)
+ --traceback always print a traceback on exception
+ --time time how long the command takes
+ --profile print command execution profile
+ --version output version information and exit
+ -h --help display help and exit
[+] marked option can be specified multiple times
@@ -284,10 +280,10 @@
options:
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -S --subrepos recurse into subrepositories
- -n --dry-run do not perform actions, just print output
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -S --subrepos recurse into subrepositories
+ -n --dry-run do not perform actions, just print output
[+] marked option can be specified multiple times
@@ -323,30 +319,32 @@
options:
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -S --subrepos recurse into subrepositories
- -n --dry-run do not perform actions, just print output
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -S --subrepos recurse into subrepositories
+ -n --dry-run do not perform actions, just print output
+
+ [+] marked option can be specified multiple times
global options:
- -R --repository REPO repository root directory or name of overlay bundle
- file
- --cwd DIR change working directory
- -y --noninteractive do not prompt, automatically pick the first choice
- for all prompts
- -q --quiet suppress output
- -v --verbose enable additional output
- --config CONFIG [+] set/override config option (use
- 'section.name=value')
- --debug enable debugging output
- --debugger start debugger
- --encoding ENCODE set the charset encoding (default: ascii)
- --encodingmode MODE set the charset encoding mode (default: strict)
- --traceback always print a traceback on exception
- --time time how long the command takes
- --profile print command execution profile
- --version output version information and exit
- -h --help display help and exit
+
+ -R --repository REPO repository root directory or name of overlay bundle
+ file
+ --cwd DIR change working directory
+ -y --noninteractive do not prompt, automatically pick the first choice for
+ all prompts
+ -q --quiet suppress output
+ -v --verbose enable additional output
+ --config CONFIG [+] set/override config option (use 'section.name=value')
+ --debug enable debugging output
+ --debugger start debugger
+ --encoding ENCODE set the charset encoding (default: ascii)
+ --encodingmode MODE set the charset encoding mode (default: strict)
+ --traceback always print a traceback on exception
+ --time time how long the command takes
+ --profile print command execution profile
+ --version output version information and exit
+ -h --help display help and exit
[+] marked option can be specified multiple times
@@ -359,32 +357,6 @@
Copyright (C) 2005-2011 Matt Mackall and others
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
- hg add [OPTION]... [FILE]...
-
- add the specified files on the next commit
-
- Schedule files to be version controlled and added to the repository.
-
- The files will be added to the repository at the next commit. To undo an
- add before that, see "hg forget".
-
- If no names are given, add all files to the repository.
-
- Returns 0 if all files are successfully added.
-
- use "hg -v help add" to show verbose help
-
- options:
-
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -S --subrepos recurse into subrepositories
- -n --dry-run do not perform actions, just print output
-
- [+] marked option can be specified multiple times
-
- use "hg -v help add" to show global options
$ hg add --skjdfks
hg add: option --skjdfks not recognized
@@ -394,10 +366,10 @@
options:
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -S --subrepos recurse into subrepositories
- -n --dry-run do not perform actions, just print output
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -S --subrepos recurse into subrepositories
+ -n --dry-run do not perform actions, just print output
[+] marked option can be specified multiple times
@@ -463,23 +435,25 @@
Returns 0 on success.
+ use "hg -v help diff" to show verbose help
+
options:
- -r --rev REV [+] revision
- -c --change REV change made by revision
- -a --text treat all files as text
- -g --git use git extended diff format
- --nodates omit dates from diff headers
- -p --show-function show which function each change is in
- --reverse produce a diff that undoes the changes
- -w --ignore-all-space ignore white space when comparing lines
- -b --ignore-space-change ignore changes in the amount of white space
- -B --ignore-blank-lines ignore changes whose lines are all blank
- -U --unified NUM number of lines of context to show
- --stat output diffstat-style summary of changes
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -S --subrepos recurse into subrepositories
+ -r --rev REV [+] revision
+ -c --change REV change made by revision
+ -a --text treat all files as text
+ -g --git use git extended diff format
+ --nodates omit dates from diff headers
+ -p --show-function show which function each change is in
+ --reverse produce a diff that undoes the changes
+ -w --ignore-all-space ignore white space when comparing lines
+ -b --ignore-space-change ignore changes in the amount of white space
+ -B --ignore-blank-lines ignore changes whose lines are all blank
+ -U --unified NUM number of lines of context to show
+ --stat output diffstat-style summary of changes
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -S --subrepos recurse into subrepositories
[+] marked option can be specified multiple times
@@ -525,24 +499,26 @@
Returns 0 on success.
+ use "hg -v help status" to show verbose help
+
options:
- -A --all show status of all files
- -m --modified show only modified files
- -a --added show only added files
- -r --removed show only removed files
- -d --deleted show only deleted (but tracked) files
- -c --clean show only files without changes
- -u --unknown show only unknown (not tracked) files
- -i --ignored show only ignored files
- -n --no-status hide status prefix
- -C --copies show source of copied files
- -0 --print0 end filenames with NUL, for use with xargs
- --rev REV [+] show difference from revision
- --change REV list the changed files of a revision
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -S --subrepos recurse into subrepositories
+ -A --all show status of all files
+ -m --modified show only modified files
+ -a --added show only added files
+ -r --removed show only removed files
+ -d --deleted show only deleted (but tracked) files
+ -c --clean show only files without changes
+ -u --unknown show only unknown (not tracked) files
+ -i --ignored show only ignored files
+ -n --no-status hide status prefix
+ -C --copies show source of copied files
+ -0 --print0 end filenames with NUL, for use with xargs
+ --rev REV [+] show difference from revision
+ --change REV list the changed files of a revision
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -S --subrepos recurse into subrepositories
[+] marked option can be specified multiple times
--- a/tests/test-hook.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-hook.t Wed Sep 28 16:11:53 2011 -0500
@@ -277,7 +277,6 @@
(run 'hg update' to get a working copy)
$ hg rollback
repository tip rolled back to revision 3 (undo pull)
- working directory now based on revision 0
preoutgoing hook can prevent outgoing changes
--- a/tests/test-import-bypass.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-import-bypass.t Wed Sep 28 16:11:53 2011 -0500
@@ -62,7 +62,6 @@
$ hg rollback
repository tip rolled back to revision 1 (undo commit)
- working directory now based on revision 0
Test --import-branch
@@ -75,7 +74,6 @@
$ hg rollback
repository tip rolled back to revision 1 (undo commit)
- working directory now based on revision 0
Test --strip
@@ -98,7 +96,6 @@
applying patch from stdin
$ hg rollback
repository tip rolled back to revision 1 (undo commit)
- working directory now based on revision 0
Test unsupported combinations
--- a/tests/test-init.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-init.t Wed Sep 28 16:11:53 2011 -0500
@@ -19,8 +19,8 @@
store created
00changelog.i created
revlogv1
+ fncache
store
- fncache
dotencode
$ echo this > local/foo
$ hg ci --cwd local -A -m "init"
@@ -48,8 +48,8 @@
store created
00changelog.i created
revlogv1
+ fncache
store
- fncache
test failure
@@ -145,8 +145,8 @@
store created
00changelog.i created
revlogv1
+ fncache
store
- fncache
dotencode
prepare test of init of url configured from paths
@@ -162,8 +162,8 @@
store created
00changelog.i created
revlogv1
+ fncache
store
- fncache
dotencode
verify that clone also expand urls
@@ -175,8 +175,8 @@
store created
00changelog.i created
revlogv1
+ fncache
store
- fncache
dotencode
clone bookmarks
--- a/tests/test-minirst.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-minirst.py Wed Sep 28 16:11:53 2011 -0500
@@ -231,3 +231,14 @@
"""
debugformat('comments', comments, 30)
+
+
+data = [['a', 'b', 'c'],
+ ['1', '2', '3'],
+ ['foo', 'bar', 'baz this list is very very very long man']]
+
+table = minirst.maketable(data, 2, True)
+
+print table
+
+debugformat('table', table, 30)
--- a/tests/test-minirst.py.out Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-minirst.py.out Wed Sep 28 16:11:53 2011 -0500
@@ -6,6 +6,7 @@
containing random whitespace.
The third and final paragraph.
+
----------------------------------------------------------------------
paragraphs formatted to fit within 30 characters:
@@ -19,6 +20,7 @@
whitespace.
The third and final paragraph.
+
----------------------------------------------------------------------
definitions formatted to fit within 60 characters:
@@ -33,6 +35,7 @@
A Nested/Indented Term
Definition.
+
----------------------------------------------------------------------
definitions formatted to fit within 30 characters:
@@ -52,6 +55,7 @@
A Nested/Indented Term
Definition.
+
----------------------------------------------------------------------
literals formatted to fit within 60 characters:
@@ -72,6 +76,7 @@
This literal block is started with '::',
the so-called expanded form. The paragraph
with '::' disappears in the final output.
+
----------------------------------------------------------------------
literals formatted to fit within 30 characters:
@@ -94,6 +99,7 @@
This literal block is started with '::',
the so-called expanded form. The paragraph
with '::' disappears in the final output.
+
----------------------------------------------------------------------
lists formatted to fit within 60 characters:
@@ -129,6 +135,7 @@
This is the first line. The line continues here.
This is the second line.
+
----------------------------------------------------------------------
lists formatted to fit within 30 characters:
@@ -173,6 +180,7 @@
This is the first line. The
line continues here.
This is the second line.
+
----------------------------------------------------------------------
options formatted to fit within 60 characters:
@@ -200,6 +208,7 @@
paragraph:
--foo bar baz
+
----------------------------------------------------------------------
options formatted to fit within 30 characters:
@@ -272,6 +281,7 @@
normal paragraph:
--foo bar baz
+
----------------------------------------------------------------------
fields formatted to fit within 60 characters:
@@ -286,6 +296,7 @@
here.
much too large
This key is big enough to get its own line.
+
----------------------------------------------------------------------
fields formatted to fit within 30 characters:
@@ -305,11 +316,13 @@
This key is big
enough to get its
own line.
+
----------------------------------------------------------------------
containers (normal) formatted to fit within 60 characters:
----------------------------------------------------------------------
Normal output.
+
----------------------------------------------------------------------
containers (verbose) formatted to fit within 60 characters:
@@ -317,6 +330,7 @@
Normal output.
Verbose output.
+
----------------------------------------------------------------------
['debug', 'debug']
----------------------------------------------------------------------
@@ -326,6 +340,7 @@
Normal output.
Initial debug output.
+
----------------------------------------------------------------------
['verbose']
----------------------------------------------------------------------
@@ -339,6 +354,7 @@
Verbose output.
Debug output.
+
----------------------------------------------------------------------
[]
----------------------------------------------------------------------
@@ -346,6 +362,7 @@
roles formatted to fit within 60 characters:
----------------------------------------------------------------------
Please see "hg add".
+
----------------------------------------------------------------------
sections formatted to fit within 20 characters:
@@ -361,6 +378,7 @@
Markup: "foo" and "hg help"
---------------------------
+
----------------------------------------------------------------------
admonitions formatted to fit within 30 characters:
@@ -377,6 +395,7 @@
!Danger!
This is danger
+
----------------------------------------------------------------------
comments formatted to fit within 30 characters:
@@ -386,5 +405,24 @@
Some indented text.
Empty comment above
+
----------------------------------------------------------------------
+ === === ========================================
+ a b c
+ === === ========================================
+ 1 2 3
+ foo bar baz this list is very very very long man
+ === === ========================================
+
+table formatted to fit within 30 characters:
+----------------------------------------------------------------------
+ a b c
+ ------------------------------
+ 1 2 3
+ foo bar baz this list is
+ very very very long
+ man
+
+----------------------------------------------------------------------
+
--- a/tests/test-mq-qrefresh-interactive.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-mq-qrefresh-interactive.t Wed Sep 28 16:11:53 2011 -0500
@@ -31,18 +31,18 @@
options:
- -e --edit edit commit message
- -g --git use git extended diff format
- -s --short refresh only files already in the patch and
- specified files
- -U --currentuser add/update author field in patch with current user
- -u --user USER add/update author field in patch with given user
- -D --currentdate add/update date field in patch with current date
- -d --date DATE add/update date field in patch with given date
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -m --message TEXT use text as commit message
- -l --logfile FILE read commit message from file
+ -e --edit edit commit message
+ -g --git use git extended diff format
+ -s --short refresh only files already in the patch and
+ specified files
+ -U --currentuser add/update author field in patch with current user
+ -u --user USER add/update author field in patch with given user
+ -D --currentdate add/update date field in patch with current date
+ -d --date DATE add/update date field in patch with given date
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -m --message TEXT use text as commit message
+ -l --logfile FILE read commit message from file
[+] marked option can be specified multiple times
@@ -75,19 +75,19 @@
options:
- -e --edit edit commit message
- -g --git use git extended diff format
- -s --short refresh only files already in the patch and
- specified files
- -U --currentuser add/update author field in patch with current user
- -u --user USER add/update author field in patch with given user
- -D --currentdate add/update date field in patch with current date
- -d --date DATE add/update date field in patch with given date
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -m --message TEXT use text as commit message
- -l --logfile FILE read commit message from file
- -i --interactive interactively select changes to refresh
+ -e --edit edit commit message
+ -g --git use git extended diff format
+ -s --short refresh only files already in the patch and
+ specified files
+ -U --currentuser add/update author field in patch with current user
+ -u --user USER add/update author field in patch with given user
+ -D --currentdate add/update date field in patch with current date
+ -d --date DATE add/update date field in patch with given date
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -m --message TEXT use text as commit message
+ -l --logfile FILE read commit message from file
+ -i --interactive interactively select changes to refresh
[+] marked option can be specified multiple times
--- a/tests/test-notify-changegroup.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-notify-changegroup.t Wed Sep 28 16:11:53 2011 -0500
@@ -72,4 +72,55 @@
@@ -0,0 +1,2 @@
+a
+a
+ $ hg --cwd a rollback
+ repository tip rolled back to revision -1 (undo push)
+unbundle with unrelated source
+
+ $ hg --cwd b bundle ../test.hg ../a
+ searching for changes
+ 2 changesets found
+ $ hg --cwd a unbundle ../test.hg
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 1 files
+ (run 'hg update' to get a working copy)
+ $ hg --cwd a rollback
+ repository tip rolled back to revision -1 (undo unbundle)
+
+unbundle with correct source
+
+ $ hg --config notify.sources=unbundle --cwd a unbundle ../test.hg 2>&1 |
+ > python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
+ adding changesets
+ adding manifests
+ adding file changes
+ added 2 changesets with 2 changes to 1 files
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Date: * (glob)
+ Subject: * (glob)
+ From: test
+ X-Hg-Notification: changeset cb9a9f314b8b
+ Message-Id: <*> (glob)
+ To: baz, foo@bar
+
+ changeset cb9a9f314b8b in $TESTTMP/a
+ details: $TESTTMP/a?cmd=changeset;node=cb9a9f314b8b
+ summary: a
+
+ changeset ba677d0156c1 in $TESTTMP/a
+ details: $TESTTMP/a?cmd=changeset;node=ba677d0156c1
+ summary: b
+
+ diffs (6 lines):
+
+ diff -r 000000000000 -r ba677d0156c1 a
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:00 1970 +0000
+ @@ -0,0 +1,2 @@
+ +a
+ +a
+ (run 'hg update' to get a working copy)
--- a/tests/test-notify.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-notify.t Wed Sep 28 16:11:53 2011 -0500
@@ -17,67 +17,111 @@
> * = baz
> EOF
$ hg help notify
- notify extension - hooks for sending email notifications at commit/push time
-
- Subscriptions can be managed through a hgrc file. Default mode is to print
- messages to stdout, for testing and configuring.
+ notify extension - hooks for sending email push notifications
- To use, configure the notify extension and enable it in hgrc like this:
+ This extension let you run hooks sending email notifications when changesets
+ are being pushed, from the sending or receiving side.
- [extensions]
- notify =
+ First, enable the extension as explained in "hg help extensions", and register
+ the hook you want to run. "incoming" and "outgoing" hooks are run by the
+ changesets receiver while the "outgoing" one is for the sender:
[hooks]
# one email for each incoming changeset
incoming.notify = python:hgext.notify.hook
- # batch emails when many changesets incoming at one time
+ # one email for all incoming changesets
changegroup.notify = python:hgext.notify.hook
- # batch emails when many changesets outgoing at one time (client side)
+
+ # one email for all outgoing changesets
outgoing.notify = python:hgext.notify.hook
- [notify]
- # config items go here
-
- Required configuration items:
-
- config = /path/to/file # file containing subscriptions
-
- Optional configuration items:
-
- test = True # print messages to stdout for testing
- strip = 3 # number of slashes to strip for url paths
- domain = example.com # domain to use if committer missing domain
- style = ... # style file to use when formatting email
- template = ... # template to use when formatting email
- incoming = ... # template to use when run as incoming hook
- outgoing = ... # template to use when run as outgoing hook
- changegroup = ... # template to use when run as changegroup hook
- maxdiff = 300 # max lines of diffs to include (0=none, -1=all)
- maxsubject = 67 # truncate subject line longer than this
- diffstat = True # add a diffstat before the diff content
- sources = serve # notify if source of incoming changes in this list
- # (serve == ssh or http, push, pull, bundle)
- merge = False # send notification for merges (default True)
- [email]
- from = user@host.com # email address to send as if none given
- [web]
- baseurl = http://hgserver/... # root of hg web site for browsing commits
-
- The notify config file has same format as a regular hgrc file. It has two
- sections so you can express subscriptions in whatever way is handier for you.
+ Now the hooks are running, subscribers must be assigned to repositories. Use
+ the "[usersubs]" section to map repositories to a given email or the
+ "[reposubs]" section to map emails to a single repository:
[usersubs]
- # key is subscriber email, value is ","-separated list of glob patterns
+ # key is subscriber email, value is a comma-separated list of glob
+ # patterns
user@host = pattern
[reposubs]
- # key is glob pattern, value is ","-separated list of subscriber emails
+ # key is glob pattern, value is a comma-separated list of subscriber
+ # emails
pattern = user@host
- Glob patterns are matched against path to repository root.
+ Glob patterns are matched against absolute path to repository root. The
+ subscriptions can be defined in their own file and referenced with:
+
+ [notify]
+ config = /path/to/subscriptionsfile
+
+ Alternatively, they can be added to Mercurial configuration files by setting
+ the previous entry to an empty value.
+
+ At this point, notifications should be generated but will not be sent until
+ you set the "notify.test" entry to "False".
+
+ Notifications content can be tweaked with the following configuration entries:
+
+ notify.test
+ If "True", print messages to stdout instead of sending them. Default: True.
+
+ notify.sources
+ Space separated list of change sources. Notifications are sent only if it
+ includes the incoming or outgoing changes source. Incoming sources can be
+ "serve" for changes coming from http or ssh, "pull" for pulled changes,
+ "unbundle" for changes added by "hg unbundle" or "push" for changes being
+ pushed locally. Outgoing sources are the same except for "unbundle" which is
+ replaced by "bundle". Default: serve.
+
+ notify.strip
+ Number of leading slashes to strip from url paths. By default, notifications
+ references repositories with their absolute path. "notify.strip" let you
+ turn them into relative paths. For example, "notify.strip=3" will change
+ "/long/path/repository" into "repository". Default: 0.
+
+ notify.domain
+ If subscribers emails or the from email have no domain set, complete them
+ with this value.
- If you like, you can put notify config file in repository that users can push
- changes to, they can manage their own subscriptions.
+ notify.style
+ Style file to use when formatting emails.
+
+ notify.template
+ Template to use when formatting emails.
+
+ notify.incoming
+ Template to use when run as incoming hook, override "notify.template".
+
+ notify.outgoing
+ Template to use when run as outgoing hook, override "notify.template".
+
+ notify.changegroup
+ Template to use when running as changegroup hook, override
+ "notify.template".
+
+ notify.maxdiff
+ Maximum number of diff lines to include in notification email. Set to 0 to
+ disable the diff, -1 to include all of it. Default: 300.
+
+ notify.maxsubject
+ Maximum number of characters in emails subject line. Default: 67.
+
+ notify.diffstat
+ Set to True to include a diffstat before diff content. Default: True.
+
+ notify.merge
+ If True, send notifications for merge changesets. Default: True.
+
+ If set, the following entries will also be used to customize the
+ notifications:
+
+ email.from
+ Email "From" address to use if none can be found in generated email content.
+
+ web.baseurl
+ Root repository browsing URL to combine with repository paths when making
+ references. See also "notify.strip".
no commands defined
$ hg init a
@@ -156,7 +200,6 @@
$ hg --cwd b rollback
repository tip rolled back to revision 0 (undo pull)
- working directory now based on revision 0
$ hg --cwd b pull ../a 2>&1 | grep 'error.*\.notify\.conf' > /dev/null && echo pull failed
pull failed
$ touch ".notify.conf"
@@ -165,7 +208,6 @@
$ hg --cwd b rollback
repository tip rolled back to revision 0 (undo pull)
- working directory now based on revision 0
$ hg --traceback --cwd b pull ../a | \
> python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
pulling from ../a
@@ -210,7 +252,6 @@
$ hg --cwd b rollback
repository tip rolled back to revision 0 (undo pull)
- working directory now based on revision 0
$ hg --traceback --cwd b pull ../a | \
> python -c 'import sys,re; print re.sub("\n\t", " ", sys.stdin.read()),'
pulling from ../a
--- a/tests/test-patchbomb.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-patchbomb.t Wed Sep 28 16:11:53 2011 -0500
@@ -1469,13 +1469,71 @@
+ff2c9fa2018b15fa74b33363bda9527323e2a99f two
+ff2c9fa2018b15fa74b33363bda9527323e2a99f two.diff
-
+no intro message in non-interactive mode
$ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar --in-reply-to baz \
- > -r 0:1
+ > -r 0:1 | fixheaders
This patch series consists of 2 patches.
- abort: Subject: [PATCH 0 of 2] Please enter a valid value
- [255]
+ Subject: [PATCH 0 of 2]
+
+ Displaying [PATCH 1 of 2] a ...
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Subject: [PATCH 1 of 2] a
+ X-Mercurial-Node: 8580ff50825a50c8f716709acdf8de0deddcd6ab
+ Message-Id: <8580ff50825a50c8f716.60@
+ In-Reply-To: <baz>
+ References: <baz>
+ User-Agent: Mercurial-patchbomb
+ Date: Thu, 01 Jan 1970 00:01:00 +0000
+ From: quux
+ To: foo
+ Cc: bar
+
+ # HG changeset patch
+ # User test
+ # Date 1 0
+ # Node ID 8580ff50825a50c8f716709acdf8de0deddcd6ab
+ # Parent 0000000000000000000000000000000000000000
+ a
+
+ diff -r 000000000000 -r 8580ff50825a a
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/a Thu Jan 01 00:00:01 1970 +0000
+ @@ -0,0 +1,1 @@
+ +a
+
+ Displaying [PATCH 2 of 2] b ...
+ Content-Type: text/plain; charset="us-ascii"
+ MIME-Version: 1.0
+ Content-Transfer-Encoding: 7bit
+ Subject: [PATCH 2 of 2] b
+ X-Mercurial-Node: 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+ Message-Id: <97d72e5f12c7e84f8506.61@
+ In-Reply-To: <8580ff50825a50c8f716.60@
+ References: <8580ff50825a50c8f716.60@
+ User-Agent: Mercurial-patchbomb
+ Date: Thu, 01 Jan 1970 00:01:01 +0000
+ From: quux
+ To: foo
+ Cc: bar
+
+ # HG changeset patch
+ # User test
+ # Date 2 0
+ # Node ID 97d72e5f12c7e84f85064aa72e5a297142c36ed9
+ # Parent 8580ff50825a50c8f716709acdf8de0deddcd6ab
+ b
+
+ diff -r 8580ff50825a -r 97d72e5f12c7 b
+ --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+ +++ b/b Thu Jan 01 00:00:02 1970 +0000
+ @@ -0,0 +1,1 @@
+ +b
+
+
+
$ hg email --date '1970-1-1 0:1' -n -f quux -t foo -c bar --in-reply-to baz \
> -s test -r 0:1 | fixheaders
--- a/tests/test-progress.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-progress.t Wed Sep 28 16:11:53 2011 -0500
@@ -9,16 +9,28 @@
> total = loops
> if opts.get('total', None):
> total = int(opts.get('total'))
+ > nested = False
+ > if opts.get('nested', None):
+ > nested = True
> loops = abs(loops)
>
> for i in range(loops):
> ui.progress('loop', i, 'loop.%d' % i, 'loopnum', total)
+ > if opts.get('parallel'):
+ > ui.progress('other', i, 'other.%d' % i, 'othernum', total)
+ > if nested:
+ > for j in range(2):
+ > ui.progress('nested', j, 'nested.%d' % j, 'nestnum', 2)
+ > ui.progress('nested', None, 'nested.done', 'nestnum', 2)
> ui.progress('loop', None, 'loop.done', 'loopnum', total)
>
> commands.norepo += " loop"
>
> cmdtable = {
- > "loop": (loop, [('', 'total', '', 'override for total')],
+ > "loop": (loop, [('', 'total', '', 'override for total'),
+ > ('', 'nested', False, 'show nested results'),
+ > ('', 'parallel', False, 'show parallel sets of results'),
+ > ],
> 'hg loop LOOPS'),
> }
> EOF
@@ -47,6 +59,42 @@
loop [===============================> ] 2/3
\r (esc)
+
+test nested short-lived topics (which shouldn't display with nestdelay):
+
+ $ hg -y loop 3 --nested 2>&1 | \
+ > python $TESTDIR/filtercr.py
+
+ loop [ ] 0/3
+ loop [===============> ] 1/3
+ loop [===============================> ] 2/3
+ \r (esc)
+
+
+ $ hg --config progress.changedelay=0 -y loop 3 --nested 2>&1 | \
+ > python $TESTDIR/filtercr.py
+
+ loop [ ] 0/3
+ nested [ ] 0/2
+ nested [======================> ] 1/2
+ loop [===============> ] 1/3
+ nested [ ] 0/2
+ nested [======================> ] 1/2
+ loop [===============================> ] 2/3
+ nested [ ] 0/2
+ nested [======================> ] 1/2
+ \r (esc)
+
+
+test two topics being printed in parallel (as when we're doing a local
+--pull clone, where you get the unbundle and bundle progress at the
+same time):
+ $ hg loop 3 --parallel 2>&1 | python $TESTDIR/filtercr.py
+
+ loop [ ] 0/3
+ loop [===============> ] 1/3
+ loop [===============================> ] 2/3
+ \r (esc)
test refresh is taken in account
$ hg -y --config progress.refresh=100 loop 3 2>&1 | $TESTDIR/filtercr.py
--- a/tests/test-push-http.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-push-http.t Wed Sep 28 16:11:53 2011 -0500
@@ -64,7 +64,6 @@
% serve errors
$ hg rollback
repository tip rolled back to revision 0 (undo serve)
- working directory now based on revision 0
expect success, server lacks the httpheader capability
@@ -81,7 +80,6 @@
% serve errors
$ hg rollback
repository tip rolled back to revision 0 (undo serve)
- working directory now based on revision 0
expect success, server lacks the unbundlehash capability
@@ -98,7 +96,6 @@
% serve errors
$ hg rollback
repository tip rolled back to revision 0 (undo serve)
- working directory now based on revision 0
expect authorization error: all users denied
--- a/tests/test-qrecord.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-qrecord.t Wed Sep 28 16:11:53 2011 -0500
@@ -54,19 +54,19 @@
options:
- -A --addremove mark new/missing files as added/removed before
- committing
- --close-branch mark a branch as closed, hiding it from the branch
- list
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -m --message TEXT use text as commit message
- -l --logfile FILE read commit message from file
- -d --date DATE record the specified date as commit date
- -u --user USER record the specified user as committer
- -w --ignore-all-space ignore white space when comparing lines
- -b --ignore-space-change ignore changes in the amount of white space
- -B --ignore-blank-lines ignore changes whose lines are all blank
+ -A --addremove mark new/missing files as added/removed before
+ committing
+ --close-branch mark a branch as closed, hiding it from the branch
+ list
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -m --message TEXT use text as commit message
+ -l --logfile FILE read commit message from file
+ -d --date DATE record the specified date as commit date
+ -u --user USER record the specified user as committer
+ -w --ignore-all-space ignore white space when comparing lines
+ -b --ignore-space-change ignore changes in the amount of white space
+ -B --ignore-blank-lines ignore changes whose lines are all blank
[+] marked option can be specified multiple times
@@ -129,20 +129,20 @@
options:
- -e --edit edit commit message
- -g --git use git extended diff format
- -U --currentuser add "From: <current user>" to patch
- -u --user USER add "From: <USER>" to patch
- -D --currentdate add "Date: <current date>" to patch
- -d --date DATE add "Date: <DATE>" to patch
- -I --include PATTERN [+] include names matching the given patterns
- -X --exclude PATTERN [+] exclude names matching the given patterns
- -m --message TEXT use text as commit message
- -l --logfile FILE read commit message from file
- -w --ignore-all-space ignore white space when comparing lines
- -b --ignore-space-change ignore changes in the amount of white space
- -B --ignore-blank-lines ignore changes whose lines are all blank
- --mq operate on patch repository
+ -e --edit edit commit message
+ -g --git use git extended diff format
+ -U --currentuser add "From: <current user>" to patch
+ -u --user USER add "From: <USER>" to patch
+ -D --currentdate add "Date: <current date>" to patch
+ -d --date DATE add "Date: <DATE>" to patch
+ -I --include PATTERN [+] include names matching the given patterns
+ -X --exclude PATTERN [+] exclude names matching the given patterns
+ -m --message TEXT use text as commit message
+ -l --logfile FILE read commit message from file
+ -w --ignore-all-space ignore white space when comparing lines
+ -b --ignore-space-change ignore changes in the amount of white space
+ -B --ignore-blank-lines ignore changes whose lines are all blank
+ --mq operate on patch repository
[+] marked option can be specified multiple times
--- a/tests/test-rebase-detach.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-rebase-detach.t Wed Sep 28 16:11:53 2011 -0500
@@ -281,3 +281,25 @@
|/
o 0: 'A'
+
+ $ hg rebase -d 5 -s 7
+ saved backup bundle to $TESTTMP/a5/.hg/strip-backup/13547172c9c0-backup.hg
+ $ hg tglog
+ @ 8: 'D'
+ |
+ o 7: 'C'
+ |
+ | o 6: 'B'
+ |/
+ o 5: 'extra branch'
+
+ o 4: 'H'
+ |
+ | o 3: 'G'
+ |/|
+ o | 2: 'F'
+ | |
+ | o 1: 'E'
+ |/
+ o 0: 'A'
+
--- a/tests/test-rebase-parameters.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-rebase-parameters.t Wed Sep 28 16:11:53 2011 -0500
@@ -51,7 +51,7 @@
$ cd a1
$ hg rebase -s 8 -d 7
- abort: source is descendant of destination
+ abort: source is a child of destination
[255]
$ hg rebase --continue --abort
--- a/tests/test-rebase-scenario-global.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-rebase-scenario-global.t Wed Sep 28 16:11:53 2011 -0500
@@ -212,7 +212,7 @@
$ cd a7
$ hg rebase -s 6 -d 5
- abort: source is descendant of destination
+ abort: source is a child of destination
[255]
F onto G - rebase onto a descendant:
@@ -248,3 +248,25 @@
nothing to rebase
[1]
+C onto A - rebase onto an ancestor:
+
+ $ hg rebase -d 0 -s 2
+ saved backup bundle to $TESTTMP/a7/.hg/strip-backup/5fddd98957c8-backup.hg
+ $ hg tglog
+ @ 7: 'D'
+ |
+ o 6: 'C'
+ |
+ | o 5: 'H'
+ | |
+ | | o 4: 'G'
+ | |/|
+ | o | 3: 'F'
+ |/ /
+ | o 2: 'E'
+ |/
+ | o 1: 'B'
+ |/
+ o 0: 'A'
+
+
--- a/tests/test-remove.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-remove.t Wed Sep 28 16:11:53 2011 -0500
@@ -29,7 +29,7 @@
$ echo b > bar
$ hg add bar
$ remove bar
- not removing bar: file has been marked for add (use -f to force removal)
+ not removing bar: file has been marked for add (use forget to undo)
exit code: 1
A bar
./bar
--- a/tests/test-rollback.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-rollback.t Wed Sep 28 16:11:53 2011 -0500
@@ -1,9 +1,9 @@
-
+setup repo
$ hg init t
$ cd t
$ echo a > a
- $ hg add a
- $ hg commit -m "test"
+ $ hg commit -Am'add a'
+ adding a
$ hg verify
checking changesets
checking manifests
@@ -11,12 +11,14 @@
checking files
1 files, 1 changesets, 1 total revisions
$ hg parents
- changeset: 0:acb14030fe0a
+ changeset: 0:1f0dee641bb7
tag: tip
user: test
date: Thu Jan 01 00:00:00 1970 +0000
- summary: test
+ summary: add a
+
+rollback to null revision
$ hg status
$ hg rollback
repository tip rolled back to revision -1 (undo commit)
@@ -31,22 +33,23 @@
$ hg status
A a
-Test issue 902
+Two changesets this time so we rollback to a real changeset
+ $ hg commit -m'add a again'
+ $ echo a >> a
+ $ hg commit -m'modify a'
- $ hg commit -m "test2"
+Test issue 902 (current branch is preserved)
$ hg branch test
marked working directory as branch test
$ hg rollback
- repository tip rolled back to revision -1 (undo commit)
- working directory now based on revision -1
+ repository tip rolled back to revision 0 (undo commit)
+ working directory now based on revision 0
$ hg branch
default
Test issue 1635 (commit message saved)
-.hg/last-message.txt:
-
$ cat .hg/last-message.txt ; echo
- test2
+ modify a
Test rollback of hg before issue 902 was fixed
@@ -55,12 +58,41 @@
marked working directory as branch test
$ rm .hg/undo.branch
$ hg rollback
- repository tip rolled back to revision -1 (undo commit)
- named branch could not be reset, current branch is still: test
- working directory now based on revision -1
+ repository tip rolled back to revision 0 (undo commit)
+ named branch could not be reset: current branch is still 'test'
+ working directory now based on revision 0
$ hg branch
test
+working dir unaffected by rollback: do not restore dirstate et. al.
+ $ hg log --template '{rev} {branch} {desc|firstline}\n'
+ 0 default add a again
+ $ hg status
+ M a
+ $ hg bookmark foo
+ $ hg commit -m'modify a again'
+ $ echo b > b
+ $ hg commit -Am'add b'
+ adding b
+ $ hg log --template '{rev} {branch} {desc|firstline}\n'
+ 2 test add b
+ 1 test modify a again
+ 0 default add a again
+ $ hg update default
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ hg bookmark bar
+ $ cat .hg/undo.branch ; echo
+ test
+ $ hg rollback
+ repository tip rolled back to revision 1 (undo commit)
+ $ hg id -n
+ 0
+ $ hg branch
+ default
+ $ cat .hg/bookmarks.current ; echo
+ bar
+ $ hg bookmark --delete foo
+
rollback by pretxncommit saves commit message (issue 1635)
$ echo a >> a
@@ -69,9 +101,6 @@
rollback completed
abort: pretxncommit hook exited with status * (glob)
[255]
-
-.hg/last-message.txt:
-
$ cat .hg/last-message.txt ; echo
precious commit message
@@ -102,18 +131,18 @@
adding changesets
adding manifests
adding file changes
- added 1 changesets with 1 changes to 1 files
- updating to branch test
+ added 3 changesets with 2 changes to 1 files (+1 heads)
+ updating to branch default
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd u
$ hg id default
- 1df294f7b1a2
+ 068774709090
now rollback and observe that 'hg serve' reloads the repository and
presents the correct tip changeset:
$ hg -R ../t rollback
- repository tip rolled back to revision -1 (undo commit)
- working directory now based on revision -1
+ repository tip rolled back to revision 1 (undo commit)
+ working directory now based on revision 0
$ hg id default
- 000000000000
+ 791dd2169706
--- a/tests/test-setdiscovery.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-setdiscovery.t Wed Sep 28 16:11:53 2011 -0500
@@ -288,7 +288,7 @@
reading DAG from stdin
$ hg heads -t --template . | wc -c
- *261 (re)
+ \s*261 (re)
$ hg clone -b a . a
adding changesets
--- a/tests/test-share.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-share.t Wed Sep 28 16:11:53 2011 -0500
@@ -28,6 +28,15 @@
$ cat .hg/sharedpath; echo
$TESTTMP/repo1/.hg
+trailing newline on .hg/sharedpath is ok
+ $ hg tip -q
+ 0:d3873e73d99e
+ $ echo '' >> .hg/sharedpath
+ $ cat .hg/sharedpath
+ $TESTTMP/repo1/.hg
+ $ hg tip -q
+ 0:d3873e73d99e
+
commit in shared clone
$ echo a >> a
@@ -97,3 +106,21 @@
-rw-r--r-- 2 b
+
+test unshare command
+
+ $ hg unshare
+ $ test -d .hg/store
+ $ test -f .hg/sharedpath
+ [1]
+ $ hg unshare
+ abort: this is not a shared repo
+ [255]
+
+check that a change does not propagate
+
+ $ echo b >> b
+ $ hg commit -m'change in unshared'
+ $ cd ../repo1
+ $ hg id -r tip
+ c2e0ac586386 tip
--- a/tests/test-subrepo-paths.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-subrepo-paths.t Wed Sep 28 16:11:53 2011 -0500
@@ -1,19 +1,22 @@
$ hg init outer
$ cd outer
+ $ echo '[paths]' >> .hg/hgrc
+ $ echo 'default = http://example.net/' >> .hg/hgrc
+
hg debugsub with no remapping
- $ echo 'sub = http://example.net/libfoo' > .hgsub
+ $ echo 'sub = libfoo' > .hgsub
$ hg add .hgsub
$ hg debugsub
path sub
- source http://example.net/libfoo
+ source libfoo
revision
hg debugsub with remapping
- $ echo '[subpaths]' > .hg/hgrc
+ $ echo '[subpaths]' >> .hg/hgrc
$ printf 'http://example.net/lib(.*) = C:\\libs\\\\1-lib\\\n' >> .hg/hgrc
$ hg debugsub
@@ -30,6 +33,21 @@
source C:\libs\bar-lib\
revision
+test absolute source path -- testing with a URL is important since
+standard os.path.join wont treat that as an absolute path
+
+ $ echo 'abs = http://example.net/abs' > .hgsub
+ $ hg debugsub
+ path abs
+ source http://example.net/abs
+ revision
+
+ $ echo 'abs = /abs' > .hgsub
+ $ hg debugsub
+ path abs
+ source /abs
+ revision
+
test bad subpaths pattern
$ cat > .hg/hgrc <<EOF
--- a/tests/test-symlink-os-yes-fs-no.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-symlink-os-yes-fs-no.py Wed Sep 28 16:11:53 2011 -0500
@@ -5,7 +5,7 @@
BUNDLEPATH = os.path.join(TESTDIR, 'bundles', 'test-no-symlinks.hg')
# only makes sense to test on os which supports symlinks
-if not hasattr(os, "symlink"):
+if not getattr(os, "symlink", False):
sys.exit(80) # SKIPPED_STATUS defined in run-tests.py
# clone with symlink support
--- a/tests/test-url-rev.t Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-url-rev.t Wed Sep 28 16:11:53 2011 -0500
@@ -102,7 +102,6 @@
$ cd clone
$ hg rollback
repository tip rolled back to revision 1 (undo push)
- working directory now based on revision 1
$ hg -q incoming
2:faba9097cad4
@@ -147,10 +146,6 @@
$ hg rollback
repository tip rolled back to revision 1 (undo pull)
- working directory now based on revision 1
-
- $ hg up -C 0
- 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg parents -q
0:1f0dee641bb7
--- a/tests/test-walkrepo.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/test-walkrepo.py Wed Sep 28 16:11:53 2011 -0500
@@ -5,7 +5,7 @@
from os.path import join as pjoin
u = ui.ui()
-sym = hasattr(os, 'symlink') and hasattr(os.path, 'samestat')
+sym = getattr(os, 'symlink', False) and getattr(os.path, 'samestat', False)
hg.repository(u, 'top1', create=1)
mkdir('subdir')
--- a/tests/tinyproxy.py Tue Sep 20 15:21:27 2011 +0300
+++ b/tests/tinyproxy.py Wed Sep 28 16:11:53 2011 -0500
@@ -23,7 +23,8 @@
def handle(self):
(ip, port) = self.client_address
- if hasattr(self, 'allowed_clients') and ip not in self.allowed_clients:
+ allowed = getattr(self, 'allowed_clients', None)
+ if allowed is not None and ip not in allowed:
self.raw_requestline = self.rfile.readline()
if self.parse_request():
self.send_error(403)