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 ..