--- a/hgext/eol.py Wed Jul 13 19:30:09 2011 -0500
+++ b/hgext/eol.py Wed Jul 13 19:30:27 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/keyword.py Wed Jul 13 19:30:09 2011 -0500
+++ b/hgext/keyword.py Wed Jul 13 19:30:27 2011 -0500
@@ -322,11 +322,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'))
@@ -340,7 +340,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'))
@@ -475,13 +475,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/notify.py Wed Jul 13 19:30:09 2011 -0500
+++ b/hgext/notify.py Wed Jul 13 19:30:27 2011 -0500
@@ -167,9 +167,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/progress.py Wed Jul 13 19:30:09 2011 -0500
+++ b/hgext/progress.py Wed Jul 13 19:30:27 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/mercurial/bookmarks.py Wed Jul 13 19:30:09 2011 -0500
+++ b/mercurial/bookmarks.py Wed Jul 13 19:30:27 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)
--- a/mercurial/commands.py Wed Jul 13 19:30:09 2011 -0500
+++ b/mercurial/commands.py Wed Jul 13 19:30:27 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'))
@@ -349,9 +353,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
@@ -1102,8 +1105,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:
@@ -3505,10 +3508,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
@@ -3587,7 +3590,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', '')
@@ -4049,9 +4052,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
--- a/mercurial/dispatch.py Wed Jul 13 19:30:09 2011 -0500
+++ b/mercurial/dispatch.py Wed Jul 13 19:30:27 2011 -0500
@@ -474,7 +474,7 @@
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)
--- a/mercurial/hg.py Wed Jul 13 19:30:09 2011 -0500
+++ b/mercurial/hg.py Wed Jul 13 19:30:27 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):
--- a/mercurial/osutil.c Wed Jul 13 19:30:09 2011 -0500
+++ b/mercurial/osutil.c Wed Jul 13 19:30:27 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/ui.py Wed Jul 13 19:30:09 2011 -0500
+++ b/mercurial/ui.py Wed Jul 13 19:30:27 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)
--- a/mercurial/verify.py Wed Jul 13 19:30:09 2011 -0500
+++ b/mercurial/verify.py Wed Jul 13 19:30:27 2011 -0500
@@ -176,6 +176,8 @@
for f in sorted(filelinkrevs):
count += 1
+ if m == nullid:
+ continue
ui.progress(_('crosschecking'), count, total=total)
if f not in filenodes:
lr = filelinkrevs[f][0]
--- a/tests/run-tests.py Wed Jul 13 19:30:09 2011 -0500
+++ b/tests/run-tests.py Wed Jul 13 19:30:27 2011 -0500
@@ -726,6 +726,7 @@
rename(testpath + ".err", testpath)
else:
rename(testpath + ".err", testpath + ".out")
+ result('p', test)
return
result('f', (test, msg))
--- a/tests/test-bookmarks.t Wed Jul 13 19:30:09 2011 -0500
+++ b/tests/test-bookmarks.t Wed Jul 13 19:30:27 2011 -0500
@@ -342,3 +342,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-convert-svn-move.t Wed Jul 13 19:30:09 2011 -0500
+++ b/tests/test-convert-svn-move.t Wed Jul 13 19:30:27 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-debugcomplete.t Wed Jul 13 19:30:09 2011 -0500
+++ b/tests/test-debugcomplete.t Wed Jul 13 19:30:27 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-eol.t Wed Jul 13 19:30:09 2011 -0500
+++ b/tests/test-eol.t Wed Jul 13 19:30:27 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-notify-changegroup.t Wed Jul 13 19:30:09 2011 -0500
+++ b/tests/test-notify-changegroup.t Wed Jul 13 19:30:27 2011 -0500
@@ -72,4 +72,57 @@
@@ -0,0 +1,2 @@
+a
+a
+ $ hg --cwd a rollback
+ repository tip rolled back to revision -1 (undo push)
+ working directory now based on revision -1
+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)
+ working directory now based on revision -1
+
+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-progress.t Wed Jul 13 19:30:09 2011 -0500
+++ b/tests/test-progress.t Wed Jul 13 19:30:27 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