--- a/mercurial/commands.py Wed Aug 15 12:12:21 2012 +0200
+++ b/mercurial/commands.py Wed Aug 15 23:03:40 2012 +0200
@@ -1847,14 +1847,17 @@
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]'))
--- a/mercurial/fileset.py Wed Aug 15 12:12:21 2012 +0200
+++ b/mercurial/fileset.py Wed Aug 15 23:03:40 2012 +0200
@@ -107,6 +107,11 @@
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 @@
"""``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 @@
'symbol': stringset,
'and': andset,
'or': orset,
+ 'minus': minusset,
'list': listset,
'group': getset,
'not': notset,
@@ -424,7 +433,14 @@
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 @@
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 @@
subset.extend(c)
else:
status = None
- subset = ctx.walk(ctx.match([]))
+ subset = list(ctx.walk(ctx.match([])))
return getset(matchctx(ctx, subset, status), tree)
--- a/tests/test-cat.t Wed Aug 15 12:12:21 2012 +0200
+++ b/tests/test-cat.t Wed Aug 15 23:03:40 2012 +0200
@@ -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
+
--- a/tests/test-debugcomplete.t Wed Aug 15 12:12:21 2012 +0200
+++ b/tests/test-debugcomplete.t Wed Aug 15 23:03:40 2012 +0200
@@ -229,7 +229,7 @@
debugdata: changelog, manifest
debugdate: extended
debugdiscovery: old, nonheads, ssh, remotecmd, insecure
- debugfileset:
+ debugfileset: rev
debugfsinfo:
debuggetbundle: head, common, type
debugignore:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-fileset.t Wed Aug 15 23:03:40 2012 +0200
@@ -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 <<EOF
+ > \.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
+