diff --git a/contrib/wix/dist.wxs b/contrib/wix/dist.wxs
--- a/contrib/wix/dist.wxs
+++ b/contrib/wix/dist.wxs
@@ -15,7 +15,7 @@
-
+
diff --git a/hgext/convert/common.py b/hgext/convert/common.py
--- a/hgext/convert/common.py
+++ b/hgext/convert/common.py
@@ -276,9 +276,9 @@ class commandline(object):
pass
cmdline = [util.shellquote(arg) for arg in cmdline]
if not self.ui.debugflag:
- cmdline += ['2>', util.nulldev]
+ cmdline += ['2>', os.devnull]
if closestdin:
- cmdline += ['<', util.nulldev]
+ cmdline += ['<', os.devnull]
cmdline = ' '.join(cmdline)
return cmdline
diff --git a/hgext/convert/gnuarch.py b/hgext/convert/gnuarch.py
--- a/hgext/convert/gnuarch.py
+++ b/hgext/convert/gnuarch.py
@@ -184,7 +184,7 @@ class gnuarch_source(converter_source, c
cmdline = [self.execmd, cmd]
cmdline += args
cmdline = [util.shellquote(arg) for arg in cmdline]
- cmdline += ['>', util.nulldev, '2>', util.nulldev]
+ cmdline += ['>', os.devnull, '2>', os.devnull]
cmdline = util.quotecommand(' '.join(cmdline))
self.ui.debug(cmdline, '\n')
return os.system(cmdline)
diff --git a/mercurial/cmdutil.py b/mercurial/cmdutil.py
--- a/mercurial/cmdutil.py
+++ b/mercurial/cmdutil.py
@@ -515,7 +515,7 @@ def service(opts, parentfn=None, initfn=
sys.stdout.flush()
sys.stderr.flush()
- nullfd = os.open(util.nulldev, os.O_RDWR)
+ nullfd = os.open(os.devnull, os.O_RDWR)
logfilefd = nullfd
if logfile:
logfilefd = os.open(logfile, os.O_RDWR | os.O_CREAT | os.O_APPEND)
diff --git a/mercurial/commands.py b/mercurial/commands.py
--- a/mercurial/commands.py
+++ b/mercurial/commands.py
@@ -1847,14 +1847,17 @@ def debugdiscovery(ui, repo, remoteurl="
localrevs = opts.get('local_head')
doit(localrevs, remoterevs)
-@command('debugfileset', [], ('REVSPEC'))
-def debugfileset(ui, repo, expr):
+@command('debugfileset',
+ [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
+ _('[-r REV] FILESPEC'))
+def debugfileset(ui, repo, expr, **opts):
'''parse and apply a fileset specification'''
+ ctx = scmutil.revsingle(repo, opts.get('rev'), None)
if ui.verbose:
tree = fileset.parse(expr)[0]
ui.note(tree, "\n")
- for f in fileset.getfileset(repo[None], expr):
+ for f in fileset.getfileset(ctx, expr):
ui.write("%s\n" % f)
@command('debugfsinfo', [], _('[PATH]'))
@@ -1995,6 +1998,10 @@ def debuginstall(ui):
ui.write(_(" (check that your locale is properly set)\n"))
problems += 1
+ # Python lib
+ ui.status(_("checking Python lib (%s)...\n")
+ % os.path.dirname(os.__file__))
+
# compiled modules
ui.status(_("checking installed modules (%s)...\n")
% os.path.dirname(__file__))
@@ -4266,7 +4273,7 @@ def merge(ui, repo, node=None, **opts):
hint=_("run 'hg heads .' to see heads"))
parent = repo.dirstate.p1()
- if len(nbhs) == 1:
+ if len(nbhs) <= 1:
if len(bheads) > 1:
raise util.Abort(_("heads are bookmarked - "
"please merge with an explicit rev"),
diff --git a/mercurial/fileset.py b/mercurial/fileset.py
--- a/mercurial/fileset.py
+++ b/mercurial/fileset.py
@@ -107,6 +107,11 @@ def notset(mctx, x):
s = set(getset(mctx, x))
return [r for r in mctx.subset if r not in s]
+def minusset(mctx, x, y):
+ xl = getset(mctx, x)
+ yl = set(getset(mctx, y))
+ return [f for f in xl if f not in yl]
+
def listset(mctx, a, b):
raise error.ParseError(_("can't use a list in this context"))
@@ -251,8 +256,11 @@ def grep(mctx, x):
"""``grep(regex)``
File contains the given regular expression.
"""
- pat = getstring(x, _("grep requires a pattern"))
- r = re.compile(pat)
+ try:
+ # i18n: "grep" is a keyword
+ r = re.compile(getstring(x, _("grep requires a pattern")))
+ except re.error, e:
+ raise error.ParseError(_('invalid match pattern: %s') % e)
return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
_units = dict(k=2**10, K=2**10, kB=2**10, KB=2**10,
@@ -406,6 +414,7 @@ methods = {
'symbol': stringset,
'and': andset,
'or': orset,
+ 'minus': minusset,
'list': listset,
'group': getset,
'not': notset,
@@ -424,7 +433,14 @@ class matchctx(object):
def filter(self, files):
return [f for f in files if f in self.subset]
def existing(self):
- return (f for f in self.subset if f in self.ctx)
+ if self._status is not None:
+ removed = set(self._status[3])
+ unknown = set(self._status[4] + self._status[5])
+ else:
+ removed = set()
+ unknown = set()
+ return (f for f in self.subset
+ if (f in self.ctx and f not in removed) or f in unknown)
def narrow(self, files):
return matchctx(self.ctx, self.filter(files), self._status)
@@ -438,14 +454,26 @@ def _intree(funcs, tree):
return True
return False
+# filesets using matchctx.existing()
+_existingcallers = [
+ 'binary',
+ 'exec',
+ 'grep',
+ 'size',
+ 'symlink',
+]
+
def getfileset(ctx, expr):
tree, pos = parse(expr)
if (pos != len(expr)):
raise error.ParseError(_("invalid token"), pos)
# do we need status info?
- if _intree(['modified', 'added', 'removed', 'deleted',
- 'unknown', 'ignored', 'clean'], tree):
+ if (_intree(['modified', 'added', 'removed', 'deleted',
+ 'unknown', 'ignored', 'clean'], tree) or
+ # Using matchctx.existing() on a workingctx requires us to check
+ # for deleted files.
+ (ctx.rev() is None and _intree(_existingcallers, tree))):
unknown = _intree(['unknown'], tree)
ignored = _intree(['ignored'], tree)
@@ -457,7 +485,7 @@ def getfileset(ctx, expr):
subset.extend(c)
else:
status = None
- subset = ctx.walk(ctx.match([]))
+ subset = list(ctx.walk(ctx.match([])))
return getset(matchctx(ctx, subset, status), tree)
diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1009,7 +1009,7 @@ class localrepository(object):
util.rename(self.join('undo.dirstate'), self.join('dirstate'))
try:
branch = self.opener.read('undo.branch')
- self.dirstate.setbranch(branch)
+ self.dirstate.setbranch(encoding.tolocal(branch))
except IOError:
ui.warn(_('named branch could not be reset: '
'current branch is still \'%s\'\n')
@@ -1312,6 +1312,7 @@ class localrepository(object):
matched = set(changes[0] + changes[1] + changes[2])
for f in match.files():
+ f = self.dirstate.normalize(f)
if f == '.' or f in matched or f in wctx.substate:
continue
if f in changes[3]: # missing
diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py
--- a/mercurial/obsolete.py
+++ b/mercurial/obsolete.py
@@ -52,7 +52,7 @@ The header is followed by the markers. E
cannot contain '\0'.
"""
import struct
-from mercurial import util, base85
+import util, base85
from i18n import _
# the obsolete feature is not mature enought to be enabled by default.
diff --git a/mercurial/parsers.c b/mercurial/parsers.c
--- a/mercurial/parsers.c
+++ b/mercurial/parsers.c
@@ -1084,8 +1084,10 @@ static PyObject *index_partialmatch(inde
return NULL;
}
- if (nodelen > 40)
- nodelen = 40;
+ if (nodelen > 40) {
+ PyErr_SetString(PyExc_ValueError, "key too long");
+ return NULL;
+ }
for (i = 0; i < nodelen; i++)
hexdigit(node, i);
diff --git a/mercurial/posix.py b/mercurial/posix.py
--- a/mercurial/posix.py
+++ b/mercurial/posix.py
@@ -10,7 +10,6 @@ import encoding
import os, sys, errno, stat, getpass, pwd, grp, tempfile, unicodedata
posixfile = open
-nulldev = '/dev/null'
normpath = os.path.normpath
samestat = os.path.samestat
oslink = os.link
diff --git a/mercurial/store.py b/mercurial/store.py
--- a/mercurial/store.py
+++ b/mercurial/store.py
@@ -7,7 +7,7 @@
from i18n import _
import osutil, scmutil, util
-import os, stat
+import os, stat, errno
_sha = util.sha1
@@ -398,12 +398,14 @@ class fncachestore(basicstore):
def datafiles(self):
rewrite = False
existing = []
- for f in self.fncache:
+ for f in sorted(self.fncache):
ef = self.encode(f)
try:
yield f, ef, self.getsize(ef)
existing.append(f)
- except OSError:
+ except OSError, err:
+ if err.errno != errno.ENOENT:
+ raise
# nonexistent entry
rewrite = True
if rewrite:
diff --git a/mercurial/util.py b/mercurial/util.py
--- a/mercurial/util.py
+++ b/mercurial/util.py
@@ -45,7 +45,6 @@ makedir = platform.makedir
nlinks = platform.nlinks
normpath = platform.normpath
normcase = platform.normcase
-nulldev = platform.nulldev
openhardlinks = platform.openhardlinks
oslink = platform.oslink
parsepatchoutput = platform.parsepatchoutput
diff --git a/mercurial/verify.py b/mercurial/verify.py
--- a/mercurial/verify.py
+++ b/mercurial/verify.py
@@ -120,6 +120,7 @@ def _verify(repo):
havemf = len(mf) > 0
ui.status(_("checking changesets\n"))
+ hasmanifest = False
seen = {}
checklog(cl, "changelog", 0)
total = len(repo)
@@ -130,16 +131,22 @@ def _verify(repo):
try:
changes = cl.read(n)
- mflinkrevs.setdefault(changes[0], []).append(i)
+ if changes[0] != nullid:
+ mflinkrevs.setdefault(changes[0], []).append(i)
+ hasmanifest = True
for f in changes[3]:
filelinkrevs.setdefault(f, []).append(i)
except Exception, inst:
+ hasmanifest = True
exc(i, _("unpacking changeset %s") % short(n), inst)
ui.progress(_('checking'), None)
ui.status(_("checking manifests\n"))
seen = {}
- checklog(mf, "manifest", 0)
+ if hasmanifest:
+ # Do not check manifest if there are only changelog entries with
+ # null manifests.
+ checklog(mf, "manifest", 0)
total = len(mf)
for i in mf:
ui.progress(_('checking'), i, total=total, unit=_('manifests'))
diff --git a/mercurial/windows.py b/mercurial/windows.py
--- a/mercurial/windows.py
+++ b/mercurial/windows.py
@@ -24,7 +24,6 @@ termwidth = win32.termwidth
testpid = win32.testpid
unlink = win32.unlink
-nulldev = 'NUL:'
umask = 0022
# wrap osutil.posixfile to provide friendlier exceptions
@@ -174,7 +173,7 @@ def popen(command, mode='r'):
# Work around "popen spawned process may not write to stdout
# under windows"
# http://bugs.python.org/issue1366
- command += " 2> %s" % nulldev
+ command += " 2> %s" % os.devnull
return os.popen(quotecommand(command), mode)
def explainexit(code):
diff --git a/tests/test-bookmarks-merge.t b/tests/test-bookmarks-merge.t
--- a/tests/test-bookmarks-merge.t
+++ b/tests/test-bookmarks-merge.t
@@ -91,3 +91,38 @@
b 1:d2ae7f538514
c 3:b8f96cf4688b
* e 7:ca784329f0ba
+
+# test warning when all heads are inactive bookmarks
+
+ $ hg up -C 6
+ 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
+ $ echo g > g
+ $ hg commit -Am 'g'
+ adding g
+ $ hg bookmark -i g
+ $ hg bookmarks
+ b 1:d2ae7f538514
+ c 3:b8f96cf4688b
+ e 7:ca784329f0ba
+ g 8:04dd21731d95
+ $ hg heads
+ changeset: 8:04dd21731d95
+ bookmark: g
+ tag: tip
+ parent: 6:be381d1126a0
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: g
+
+ changeset: 7:ca784329f0ba
+ bookmark: e
+ parent: 5:26bee9c5bcf3
+ parent: 4:a0546fcfe0fb
+ user: test
+ date: Thu Jan 01 00:00:00 1970 +0000
+ summary: merge
+
+ $ hg merge
+ abort: heads are bookmarked - please merge with an explicit rev
+ (run 'hg heads' to see all heads)
+ [255]
diff --git a/tests/test-cat.t b/tests/test-cat.t
--- a/tests/test-cat.t
+++ b/tests/test-cat.t
@@ -21,3 +21,14 @@
[1]
$ hg cat -r 1 b
1
+
+Test fileset
+
+ $ echo 3 > c
+ $ hg ci -Am addmore c
+ $ hg cat 'set:not(b) or a'
+ 3
+ $ hg cat 'set:c or b'
+ 1
+ 3
+
diff --git a/tests/test-debugcomplete.t b/tests/test-debugcomplete.t
--- a/tests/test-debugcomplete.t
+++ b/tests/test-debugcomplete.t
@@ -229,7 +229,7 @@ Show all commands + options
debugdata: changelog, manifest
debugdate: extended
debugdiscovery: old, nonheads, ssh, remotecmd, insecure
- debugfileset:
+ debugfileset: rev
debugfsinfo:
debuggetbundle: head, common, type
debugignore:
diff --git a/tests/test-encoding.t b/tests/test-encoding.t
--- a/tests/test-encoding.t
+++ b/tests/test-encoding.t
@@ -44,6 +44,10 @@ these should work
marked working directory as branch \xe9 (esc)
(branches are permanent and global, did you want a bookmark?)
$ HGENCODING=latin-1 hg ci -m 'latin1 branch'
+ $ hg -q rollback
+ $ HGENCODING=latin-1 hg branch
+ \xe9 (esc)
+ $ HGENCODING=latin-1 hg ci -m 'latin1 branch'
$ rm .hg/branch
hg log (ascii)
diff --git a/tests/test-fileset.t b/tests/test-fileset.t
new file mode 100644
--- /dev/null
+++ b/tests/test-fileset.t
@@ -0,0 +1,228 @@
+ $ fileset() {
+ > hg debugfileset "$@"
+ > }
+
+ $ hg init repo
+ $ cd repo
+ $ echo a > a1
+ $ echo a > a2
+ $ echo b > b1
+ $ echo b > b2
+ $ hg ci -Am addfiles
+ adding a1
+ adding a2
+ adding b1
+ adding b2
+
+Test operators and basic patterns
+
+ $ fileset a1
+ a1
+ $ fileset 'a*'
+ a1
+ a2
+ $ fileset '"re:a\d"'
+ a1
+ a2
+ $ fileset 'a1 or a2'
+ a1
+ a2
+ $ fileset 'a1 | a2'
+ a1
+ a2
+ $ fileset 'a* and "*1"'
+ a1
+ $ fileset 'a* & "*1"'
+ a1
+ $ fileset 'not (r"a*")'
+ b1
+ b2
+ $ fileset '! ("a*")'
+ b1
+ b2
+ $ fileset 'a* - a1'
+ a2
+
+Test files status
+
+ $ rm a1
+ $ hg rm a2
+ $ echo b >> b2
+ $ hg cp b1 c1
+ $ echo c > c2
+ $ echo c > c3
+ $ cat > .hgignore < \.hgignore
+ > 2$
+ > EOF
+ $ fileset 'modified()'
+ b2
+ $ fileset 'added()'
+ c1
+ $ fileset 'removed()'
+ a2
+ $ fileset 'deleted()'
+ a1
+ $ fileset 'unknown()'
+ c3
+ $ fileset 'ignored()'
+ .hgignore
+ c2
+ $ fileset 'hgignore()'
+ a2
+ b2
+ $ fileset 'clean()'
+ b1
+ $ fileset 'copied()'
+ c1
+
+Test files properties
+
+ >>> file('bin', 'wb').write('\0a')
+ $ fileset 'binary()'
+ $ fileset 'binary() and unknown()'
+ bin
+ $ echo '^bin$' >> .hgignore
+ $ fileset 'binary() and ignored()'
+ bin
+ $ hg add bin
+ $ fileset 'binary()'
+ bin
+
+ $ fileset 'grep("b{1}")'
+ b2
+ c1
+ b1
+ $ fileset 'grep("missingparens(")'
+ hg: parse error: invalid match pattern: unbalanced parenthesis
+ [255]
+
+#if execbit
+ $ chmod +x b2
+ $ fileset 'exec()'
+ b2
+#endif
+
+#if symlink
+ $ ln -s b2 b2link
+ $ fileset 'symlink() and unknown()'
+ b2link
+ $ hg add b2link
+#endif
+
+ >>> file('1k', 'wb').write(' '*1024)
+ >>> file('2k', 'wb').write(' '*2048)
+ $ hg add 1k 2k
+ $ fileset 'size("bar")'
+ hg: parse error: couldn't parse size: bar
+ [255]
+ $ fileset 'size(1k)'
+ 1k
+ $ fileset '(1k or 2k) and size("< 2k")'
+ 1k
+ $ fileset '(1k or 2k) and size("<=2k")'
+ 1k
+ 2k
+ $ fileset '(1k or 2k) and size("> 1k")'
+ 2k
+ $ fileset '(1k or 2k) and size(">=1K")'
+ 1k
+ 2k
+ $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
+ 1k
+
+Test merge states
+
+ $ hg ci -m manychanges
+ $ hg up -C 0
+ * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
+ $ echo c >> b2
+ $ hg ci -m diverging b2
+ created new head
+ $ fileset 'resolved()'
+ $ fileset 'unresolved()'
+ $ hg merge
+ merging b2
+ warning: conflicts during merge.
+ merging b2 incomplete! (edit conflicts, then use 'hg resolve --mark')
+ * files updated, 0 files merged, * files removed, 1 files unresolved (glob)
+ use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
+ [1]
+ $ fileset 'resolved()'
+ $ fileset 'unresolved()'
+ b2
+ $ echo e > b2
+ $ hg resolve -m b2
+ $ fileset 'resolved()'
+ b2
+ $ fileset 'unresolved()'
+ $ hg ci -m merge
+
+Test subrepo predicate
+
+ $ hg init sub
+ $ echo a > sub/suba
+ $ hg -R sub add sub/suba
+ $ hg -R sub ci -m sub
+ $ echo 'sub = sub' > .hgsub
+ $ fileset 'subrepo()'
+ $ hg add .hgsub
+ $ fileset 'subrepo()'
+ sub
+ $ fileset 'subrepo("sub")'
+ sub
+ $ fileset 'subrepo("glob:*")'
+ sub
+ $ hg ci -m subrepo
+
+Test with a revision
+
+ $ hg log -G --template '{rev} {desc}\n'
+ @ 4 subrepo
+ |
+ o 3 merge
+ |\
+ | o 2 diverging
+ | |
+ o | 1 manychanges
+ |/
+ o 0 addfiles
+
+ $ echo unknown > unknown
+ $ fileset -r1 'modified()'
+ b2
+ $ fileset -r1 'added() and c1'
+ c1
+ $ fileset -r1 'removed()'
+ a2
+ $ fileset -r1 'deleted()'
+ $ fileset -r1 'unknown()'
+ $ fileset -r1 'ignored()'
+ $ fileset -r1 'hgignore()'
+ b2
+ bin
+ $ fileset -r1 'binary()'
+ bin
+ $ fileset -r1 'size(1k)'
+ 1k
+ $ fileset -r3 'resolved()'
+ $ fileset -r3 'unresolved()'
+
+#if execbit
+ $ fileset -r1 'exec()'
+ b2
+#endif
+
+#if symlink
+ $ fileset -r1 'symlink()'
+ b2link
+#endif
+
+ $ fileset -r4 'subrepo("re:su.*")'
+ sub
+ $ fileset -r4 'subrepo("sub")'
+ sub
+ $ fileset -r4 'b2 or c1'
+ b2
+ c1
+
diff --git a/tests/test-install.t b/tests/test-install.t
--- a/tests/test-install.t
+++ b/tests/test-install.t
@@ -1,6 +1,7 @@
hg debuginstall
$ hg debuginstall
checking encoding (ascii)...
+ checking Python lib (*lib*)... (glob)
checking installed modules (*mercurial)... (glob)
checking templates (*mercurial?templates)... (glob)
checking commit editor...
@@ -10,6 +11,7 @@ hg debuginstall
hg debuginstall with no username
$ HGUSER= hg debuginstall
checking encoding (ascii)...
+ checking Python lib (*lib*)... (glob)
checking installed modules (*mercurial)... (glob)
checking templates (*mercurial?templates)... (glob)
checking commit editor...
diff --git a/tests/test-verify.t b/tests/test-verify.t
--- a/tests/test-verify.t
+++ b/tests/test-verify.t
@@ -61,10 +61,22 @@ introduce some bugs in repo
$ cd ../../..
$ cd ..
-test revlog corruption
+test changelog without a manifest
$ hg init b
$ cd b
+ $ hg branch foo
+ marked working directory as branch foo
+ (branches are permanent and global, did you want a bookmark?)
+ $ hg ci -m branchfoo
+ $ hg verify
+ checking changesets
+ checking manifests
+ crosschecking files in changesets and manifests
+ checking files
+ 0 files, 1 changesets, 0 total revisions
+
+test revlog corruption
$ touch a
$ hg add a
@@ -79,12 +91,12 @@ test revlog corruption
checking manifests
crosschecking files in changesets and manifests
checking files
- a@0: broken revlog! (index data/a.i is corrupted)
+ a@1: broken revlog! (index data/a.i is corrupted)
warning: orphan revlog 'data/a.i'
- 1 files, 1 changesets, 0 total revisions
+ 1 files, 2 changesets, 0 total revisions
1 warnings encountered!
1 integrity errors encountered!
- (first damaged changeset appears to be 0)
+ (first damaged changeset appears to be 1)
[1]
$ cd ..