##// END OF EJS Templates
fileset: flatten arguments list...
Yuya Nishihara -
r38839:4dc498d6 default
parent child Browse files
Show More
@@ -1,672 +1,672 b''
1 # fileset.py - file set queries for mercurial
1 # fileset.py - file set queries for mercurial
2 #
2 #
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import errno
10 import errno
11 import re
11 import re
12
12
13 from .i18n import _
13 from .i18n import _
14 from . import (
14 from . import (
15 error,
15 error,
16 match as matchmod,
16 match as matchmod,
17 merge,
17 merge,
18 parser,
18 parser,
19 pycompat,
19 pycompat,
20 registrar,
20 registrar,
21 scmutil,
21 scmutil,
22 util,
22 util,
23 )
23 )
24 from .utils import (
24 from .utils import (
25 stringutil,
25 stringutil,
26 )
26 )
27
27
28 elements = {
28 elements = {
29 # token-type: binding-strength, primary, prefix, infix, suffix
29 # token-type: binding-strength, primary, prefix, infix, suffix
30 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
30 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
31 ":": (15, None, None, ("kindpat", 15), None),
31 ":": (15, None, None, ("kindpat", 15), None),
32 "-": (5, None, ("negate", 19), ("minus", 5), None),
32 "-": (5, None, ("negate", 19), ("minus", 5), None),
33 "not": (10, None, ("not", 10), None, None),
33 "not": (10, None, ("not", 10), None, None),
34 "!": (10, None, ("not", 10), None, None),
34 "!": (10, None, ("not", 10), None, None),
35 "and": (5, None, None, ("and", 5), None),
35 "and": (5, None, None, ("and", 5), None),
36 "&": (5, None, None, ("and", 5), None),
36 "&": (5, None, None, ("and", 5), None),
37 "or": (4, None, None, ("or", 4), None),
37 "or": (4, None, None, ("or", 4), None),
38 "|": (4, None, None, ("or", 4), None),
38 "|": (4, None, None, ("or", 4), None),
39 "+": (4, None, None, ("or", 4), None),
39 "+": (4, None, None, ("or", 4), None),
40 ",": (2, None, None, ("list", 2), None),
40 ",": (2, None, None, ("list", 2), None),
41 ")": (0, None, None, None, None),
41 ")": (0, None, None, None, None),
42 "symbol": (0, "symbol", None, None, None),
42 "symbol": (0, "symbol", None, None, None),
43 "string": (0, "string", None, None, None),
43 "string": (0, "string", None, None, None),
44 "end": (0, None, None, None, None),
44 "end": (0, None, None, None, None),
45 }
45 }
46
46
47 keywords = {'and', 'or', 'not'}
47 keywords = {'and', 'or', 'not'}
48
48
49 globchars = ".*{}[]?/\\_"
49 globchars = ".*{}[]?/\\_"
50
50
51 def tokenize(program):
51 def tokenize(program):
52 pos, l = 0, len(program)
52 pos, l = 0, len(program)
53 program = pycompat.bytestr(program)
53 program = pycompat.bytestr(program)
54 while pos < l:
54 while pos < l:
55 c = program[pos]
55 c = program[pos]
56 if c.isspace(): # skip inter-token whitespace
56 if c.isspace(): # skip inter-token whitespace
57 pass
57 pass
58 elif c in "(),-:|&+!": # handle simple operators
58 elif c in "(),-:|&+!": # handle simple operators
59 yield (c, None, pos)
59 yield (c, None, pos)
60 elif (c in '"\'' or c == 'r' and
60 elif (c in '"\'' or c == 'r' and
61 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
61 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
62 if c == 'r':
62 if c == 'r':
63 pos += 1
63 pos += 1
64 c = program[pos]
64 c = program[pos]
65 decode = lambda x: x
65 decode = lambda x: x
66 else:
66 else:
67 decode = parser.unescapestr
67 decode = parser.unescapestr
68 pos += 1
68 pos += 1
69 s = pos
69 s = pos
70 while pos < l: # find closing quote
70 while pos < l: # find closing quote
71 d = program[pos]
71 d = program[pos]
72 if d == '\\': # skip over escaped characters
72 if d == '\\': # skip over escaped characters
73 pos += 2
73 pos += 2
74 continue
74 continue
75 if d == c:
75 if d == c:
76 yield ('string', decode(program[s:pos]), s)
76 yield ('string', decode(program[s:pos]), s)
77 break
77 break
78 pos += 1
78 pos += 1
79 else:
79 else:
80 raise error.ParseError(_("unterminated string"), s)
80 raise error.ParseError(_("unterminated string"), s)
81 elif c.isalnum() or c in globchars or ord(c) > 127:
81 elif c.isalnum() or c in globchars or ord(c) > 127:
82 # gather up a symbol/keyword
82 # gather up a symbol/keyword
83 s = pos
83 s = pos
84 pos += 1
84 pos += 1
85 while pos < l: # find end of symbol
85 while pos < l: # find end of symbol
86 d = program[pos]
86 d = program[pos]
87 if not (d.isalnum() or d in globchars or ord(d) > 127):
87 if not (d.isalnum() or d in globchars or ord(d) > 127):
88 break
88 break
89 pos += 1
89 pos += 1
90 sym = program[s:pos]
90 sym = program[s:pos]
91 if sym in keywords: # operator keywords
91 if sym in keywords: # operator keywords
92 yield (sym, None, s)
92 yield (sym, None, s)
93 else:
93 else:
94 yield ('symbol', sym, s)
94 yield ('symbol', sym, s)
95 pos -= 1
95 pos -= 1
96 else:
96 else:
97 raise error.ParseError(_("syntax error"), pos)
97 raise error.ParseError(_("syntax error"), pos)
98 pos += 1
98 pos += 1
99 yield ('end', None, pos)
99 yield ('end', None, pos)
100
100
101 def parse(expr):
101 def parse(expr):
102 p = parser.parser(elements)
102 p = parser.parser(elements)
103 tree, pos = p.parse(tokenize(expr))
103 tree, pos = p.parse(tokenize(expr))
104 if pos != len(expr):
104 if pos != len(expr):
105 raise error.ParseError(_("invalid token"), pos)
105 raise error.ParseError(_("invalid token"), pos)
106 return tree
106 return parser.simplifyinfixops(tree, {'list'})
107
107
108 def getsymbol(x):
108 def getsymbol(x):
109 if x and x[0] == 'symbol':
109 if x and x[0] == 'symbol':
110 return x[1]
110 return x[1]
111 raise error.ParseError(_('not a symbol'))
111 raise error.ParseError(_('not a symbol'))
112
112
113 def getstring(x, err):
113 def getstring(x, err):
114 if x and (x[0] == 'string' or x[0] == 'symbol'):
114 if x and (x[0] == 'string' or x[0] == 'symbol'):
115 return x[1]
115 return x[1]
116 raise error.ParseError(err)
116 raise error.ParseError(err)
117
117
118 def _getkindpat(x, y, allkinds, err):
118 def _getkindpat(x, y, allkinds, err):
119 kind = getsymbol(x)
119 kind = getsymbol(x)
120 pat = getstring(y, err)
120 pat = getstring(y, err)
121 if kind not in allkinds:
121 if kind not in allkinds:
122 raise error.ParseError(_("invalid pattern kind: %s") % kind)
122 raise error.ParseError(_("invalid pattern kind: %s") % kind)
123 return '%s:%s' % (kind, pat)
123 return '%s:%s' % (kind, pat)
124
124
125 def getpattern(x, allkinds, err):
125 def getpattern(x, allkinds, err):
126 if x and x[0] == 'kindpat':
126 if x and x[0] == 'kindpat':
127 return _getkindpat(x[1], x[2], allkinds, err)
127 return _getkindpat(x[1], x[2], allkinds, err)
128 return getstring(x, err)
128 return getstring(x, err)
129
129
130 def getlist(x):
130 def getlist(x):
131 if not x:
131 if not x:
132 return []
132 return []
133 if x[0] == 'list':
133 if x[0] == 'list':
134 return getlist(x[1]) + [x[2]]
134 return list(x[1:])
135 return [x]
135 return [x]
136
136
137 def getargs(x, min, max, err):
137 def getargs(x, min, max, err):
138 l = getlist(x)
138 l = getlist(x)
139 if len(l) < min or len(l) > max:
139 if len(l) < min or len(l) > max:
140 raise error.ParseError(err)
140 raise error.ParseError(err)
141 return l
141 return l
142
142
143 def getmatch(mctx, x):
143 def getmatch(mctx, x):
144 if not x:
144 if not x:
145 raise error.ParseError(_("missing argument"))
145 raise error.ParseError(_("missing argument"))
146 return methods[x[0]](mctx, *x[1:])
146 return methods[x[0]](mctx, *x[1:])
147
147
148 def stringmatch(mctx, x):
148 def stringmatch(mctx, x):
149 return mctx.matcher([x])
149 return mctx.matcher([x])
150
150
151 def kindpatmatch(mctx, x, y):
151 def kindpatmatch(mctx, x, y):
152 return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
152 return stringmatch(mctx, _getkindpat(x, y, matchmod.allpatternkinds,
153 _("pattern must be a string")))
153 _("pattern must be a string")))
154
154
155 def andmatch(mctx, x, y):
155 def andmatch(mctx, x, y):
156 xm = getmatch(mctx, x)
156 xm = getmatch(mctx, x)
157 ym = getmatch(mctx, y)
157 ym = getmatch(mctx, y)
158 return matchmod.intersectmatchers(xm, ym)
158 return matchmod.intersectmatchers(xm, ym)
159
159
160 def ormatch(mctx, x, y):
160 def ormatch(mctx, x, y):
161 xm = getmatch(mctx, x)
161 xm = getmatch(mctx, x)
162 ym = getmatch(mctx, y)
162 ym = getmatch(mctx, y)
163 return matchmod.unionmatcher([xm, ym])
163 return matchmod.unionmatcher([xm, ym])
164
164
165 def notmatch(mctx, x):
165 def notmatch(mctx, x):
166 m = getmatch(mctx, x)
166 m = getmatch(mctx, x)
167 return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
167 return mctx.predicate(lambda f: not m(f), predrepr=('<not %r>', m))
168
168
169 def minusmatch(mctx, x, y):
169 def minusmatch(mctx, x, y):
170 xm = getmatch(mctx, x)
170 xm = getmatch(mctx, x)
171 ym = getmatch(mctx, y)
171 ym = getmatch(mctx, y)
172 return matchmod.differencematcher(xm, ym)
172 return matchmod.differencematcher(xm, ym)
173
173
174 def negatematch(mctx, x):
174 def negatematch(mctx, x):
175 raise error.ParseError(_("can't use negate operator in this context"))
175 raise error.ParseError(_("can't use negate operator in this context"))
176
176
177 def listmatch(mctx, x, y):
177 def listmatch(mctx, *xs):
178 raise error.ParseError(_("can't use a list in this context"),
178 raise error.ParseError(_("can't use a list in this context"),
179 hint=_('see hg help "filesets.x or y"'))
179 hint=_('see hg help "filesets.x or y"'))
180
180
181 def func(mctx, a, b):
181 def func(mctx, a, b):
182 funcname = getsymbol(a)
182 funcname = getsymbol(a)
183 if funcname in symbols:
183 if funcname in symbols:
184 return symbols[funcname](mctx, b)
184 return symbols[funcname](mctx, b)
185
185
186 keep = lambda fn: getattr(fn, '__doc__', None) is not None
186 keep = lambda fn: getattr(fn, '__doc__', None) is not None
187
187
188 syms = [s for (s, fn) in symbols.items() if keep(fn)]
188 syms = [s for (s, fn) in symbols.items() if keep(fn)]
189 raise error.UnknownIdentifier(funcname, syms)
189 raise error.UnknownIdentifier(funcname, syms)
190
190
191 # symbols are callable like:
191 # symbols are callable like:
192 # fun(mctx, x)
192 # fun(mctx, x)
193 # with:
193 # with:
194 # mctx - current matchctx instance
194 # mctx - current matchctx instance
195 # x - argument in tree form
195 # x - argument in tree form
196 symbols = {}
196 symbols = {}
197
197
198 # filesets using matchctx.status()
198 # filesets using matchctx.status()
199 _statuscallers = set()
199 _statuscallers = set()
200
200
201 predicate = registrar.filesetpredicate()
201 predicate = registrar.filesetpredicate()
202
202
203 @predicate('modified()', callstatus=True)
203 @predicate('modified()', callstatus=True)
204 def modified(mctx, x):
204 def modified(mctx, x):
205 """File that is modified according to :hg:`status`.
205 """File that is modified according to :hg:`status`.
206 """
206 """
207 # i18n: "modified" is a keyword
207 # i18n: "modified" is a keyword
208 getargs(x, 0, 0, _("modified takes no arguments"))
208 getargs(x, 0, 0, _("modified takes no arguments"))
209 s = set(mctx.status().modified)
209 s = set(mctx.status().modified)
210 return mctx.predicate(s.__contains__, predrepr='modified')
210 return mctx.predicate(s.__contains__, predrepr='modified')
211
211
212 @predicate('added()', callstatus=True)
212 @predicate('added()', callstatus=True)
213 def added(mctx, x):
213 def added(mctx, x):
214 """File that is added according to :hg:`status`.
214 """File that is added according to :hg:`status`.
215 """
215 """
216 # i18n: "added" is a keyword
216 # i18n: "added" is a keyword
217 getargs(x, 0, 0, _("added takes no arguments"))
217 getargs(x, 0, 0, _("added takes no arguments"))
218 s = set(mctx.status().added)
218 s = set(mctx.status().added)
219 return mctx.predicate(s.__contains__, predrepr='added')
219 return mctx.predicate(s.__contains__, predrepr='added')
220
220
221 @predicate('removed()', callstatus=True)
221 @predicate('removed()', callstatus=True)
222 def removed(mctx, x):
222 def removed(mctx, x):
223 """File that is removed according to :hg:`status`.
223 """File that is removed according to :hg:`status`.
224 """
224 """
225 # i18n: "removed" is a keyword
225 # i18n: "removed" is a keyword
226 getargs(x, 0, 0, _("removed takes no arguments"))
226 getargs(x, 0, 0, _("removed takes no arguments"))
227 s = set(mctx.status().removed)
227 s = set(mctx.status().removed)
228 return mctx.predicate(s.__contains__, predrepr='removed')
228 return mctx.predicate(s.__contains__, predrepr='removed')
229
229
230 @predicate('deleted()', callstatus=True)
230 @predicate('deleted()', callstatus=True)
231 def deleted(mctx, x):
231 def deleted(mctx, x):
232 """Alias for ``missing()``.
232 """Alias for ``missing()``.
233 """
233 """
234 # i18n: "deleted" is a keyword
234 # i18n: "deleted" is a keyword
235 getargs(x, 0, 0, _("deleted takes no arguments"))
235 getargs(x, 0, 0, _("deleted takes no arguments"))
236 s = set(mctx.status().deleted)
236 s = set(mctx.status().deleted)
237 return mctx.predicate(s.__contains__, predrepr='deleted')
237 return mctx.predicate(s.__contains__, predrepr='deleted')
238
238
239 @predicate('missing()', callstatus=True)
239 @predicate('missing()', callstatus=True)
240 def missing(mctx, x):
240 def missing(mctx, x):
241 """File that is missing according to :hg:`status`.
241 """File that is missing according to :hg:`status`.
242 """
242 """
243 # i18n: "missing" is a keyword
243 # i18n: "missing" is a keyword
244 getargs(x, 0, 0, _("missing takes no arguments"))
244 getargs(x, 0, 0, _("missing takes no arguments"))
245 s = set(mctx.status().deleted)
245 s = set(mctx.status().deleted)
246 return mctx.predicate(s.__contains__, predrepr='deleted')
246 return mctx.predicate(s.__contains__, predrepr='deleted')
247
247
248 @predicate('unknown()', callstatus=True)
248 @predicate('unknown()', callstatus=True)
249 def unknown(mctx, x):
249 def unknown(mctx, x):
250 """File that is unknown according to :hg:`status`."""
250 """File that is unknown according to :hg:`status`."""
251 # i18n: "unknown" is a keyword
251 # i18n: "unknown" is a keyword
252 getargs(x, 0, 0, _("unknown takes no arguments"))
252 getargs(x, 0, 0, _("unknown takes no arguments"))
253 s = set(mctx.status().unknown)
253 s = set(mctx.status().unknown)
254 return mctx.predicate(s.__contains__, predrepr='unknown')
254 return mctx.predicate(s.__contains__, predrepr='unknown')
255
255
256 @predicate('ignored()', callstatus=True)
256 @predicate('ignored()', callstatus=True)
257 def ignored(mctx, x):
257 def ignored(mctx, x):
258 """File that is ignored according to :hg:`status`."""
258 """File that is ignored according to :hg:`status`."""
259 # i18n: "ignored" is a keyword
259 # i18n: "ignored" is a keyword
260 getargs(x, 0, 0, _("ignored takes no arguments"))
260 getargs(x, 0, 0, _("ignored takes no arguments"))
261 s = set(mctx.status().ignored)
261 s = set(mctx.status().ignored)
262 return mctx.predicate(s.__contains__, predrepr='ignored')
262 return mctx.predicate(s.__contains__, predrepr='ignored')
263
263
264 @predicate('clean()', callstatus=True)
264 @predicate('clean()', callstatus=True)
265 def clean(mctx, x):
265 def clean(mctx, x):
266 """File that is clean according to :hg:`status`.
266 """File that is clean according to :hg:`status`.
267 """
267 """
268 # i18n: "clean" is a keyword
268 # i18n: "clean" is a keyword
269 getargs(x, 0, 0, _("clean takes no arguments"))
269 getargs(x, 0, 0, _("clean takes no arguments"))
270 s = set(mctx.status().clean)
270 s = set(mctx.status().clean)
271 return mctx.predicate(s.__contains__, predrepr='clean')
271 return mctx.predicate(s.__contains__, predrepr='clean')
272
272
273 @predicate('tracked()')
273 @predicate('tracked()')
274 def tracked(mctx, x):
274 def tracked(mctx, x):
275 """File that is under Mercurial control."""
275 """File that is under Mercurial control."""
276 # i18n: "tracked" is a keyword
276 # i18n: "tracked" is a keyword
277 getargs(x, 0, 0, _("tracked takes no arguments"))
277 getargs(x, 0, 0, _("tracked takes no arguments"))
278 return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
278 return mctx.predicate(mctx.ctx.__contains__, predrepr='tracked')
279
279
280 @predicate('binary()')
280 @predicate('binary()')
281 def binary(mctx, x):
281 def binary(mctx, x):
282 """File that appears to be binary (contains NUL bytes).
282 """File that appears to be binary (contains NUL bytes).
283 """
283 """
284 # i18n: "binary" is a keyword
284 # i18n: "binary" is a keyword
285 getargs(x, 0, 0, _("binary takes no arguments"))
285 getargs(x, 0, 0, _("binary takes no arguments"))
286 return mctx.fpredicate(lambda fctx: fctx.isbinary(),
286 return mctx.fpredicate(lambda fctx: fctx.isbinary(),
287 predrepr='binary', cache=True)
287 predrepr='binary', cache=True)
288
288
289 @predicate('exec()')
289 @predicate('exec()')
290 def exec_(mctx, x):
290 def exec_(mctx, x):
291 """File that is marked as executable.
291 """File that is marked as executable.
292 """
292 """
293 # i18n: "exec" is a keyword
293 # i18n: "exec" is a keyword
294 getargs(x, 0, 0, _("exec takes no arguments"))
294 getargs(x, 0, 0, _("exec takes no arguments"))
295 ctx = mctx.ctx
295 ctx = mctx.ctx
296 return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
296 return mctx.predicate(lambda f: ctx.flags(f) == 'x', predrepr='exec')
297
297
298 @predicate('symlink()')
298 @predicate('symlink()')
299 def symlink(mctx, x):
299 def symlink(mctx, x):
300 """File that is marked as a symlink.
300 """File that is marked as a symlink.
301 """
301 """
302 # i18n: "symlink" is a keyword
302 # i18n: "symlink" is a keyword
303 getargs(x, 0, 0, _("symlink takes no arguments"))
303 getargs(x, 0, 0, _("symlink takes no arguments"))
304 ctx = mctx.ctx
304 ctx = mctx.ctx
305 return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
305 return mctx.predicate(lambda f: ctx.flags(f) == 'l', predrepr='symlink')
306
306
307 @predicate('resolved()')
307 @predicate('resolved()')
308 def resolved(mctx, x):
308 def resolved(mctx, x):
309 """File that is marked resolved according to :hg:`resolve -l`.
309 """File that is marked resolved according to :hg:`resolve -l`.
310 """
310 """
311 # i18n: "resolved" is a keyword
311 # i18n: "resolved" is a keyword
312 getargs(x, 0, 0, _("resolved takes no arguments"))
312 getargs(x, 0, 0, _("resolved takes no arguments"))
313 if mctx.ctx.rev() is not None:
313 if mctx.ctx.rev() is not None:
314 return mctx.never()
314 return mctx.never()
315 ms = merge.mergestate.read(mctx.ctx.repo())
315 ms = merge.mergestate.read(mctx.ctx.repo())
316 return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
316 return mctx.predicate(lambda f: f in ms and ms[f] == 'r',
317 predrepr='resolved')
317 predrepr='resolved')
318
318
319 @predicate('unresolved()')
319 @predicate('unresolved()')
320 def unresolved(mctx, x):
320 def unresolved(mctx, x):
321 """File that is marked unresolved according to :hg:`resolve -l`.
321 """File that is marked unresolved according to :hg:`resolve -l`.
322 """
322 """
323 # i18n: "unresolved" is a keyword
323 # i18n: "unresolved" is a keyword
324 getargs(x, 0, 0, _("unresolved takes no arguments"))
324 getargs(x, 0, 0, _("unresolved takes no arguments"))
325 if mctx.ctx.rev() is not None:
325 if mctx.ctx.rev() is not None:
326 return mctx.never()
326 return mctx.never()
327 ms = merge.mergestate.read(mctx.ctx.repo())
327 ms = merge.mergestate.read(mctx.ctx.repo())
328 return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
328 return mctx.predicate(lambda f: f in ms and ms[f] == 'u',
329 predrepr='unresolved')
329 predrepr='unresolved')
330
330
331 @predicate('hgignore()')
331 @predicate('hgignore()')
332 def hgignore(mctx, x):
332 def hgignore(mctx, x):
333 """File that matches the active .hgignore pattern.
333 """File that matches the active .hgignore pattern.
334 """
334 """
335 # i18n: "hgignore" is a keyword
335 # i18n: "hgignore" is a keyword
336 getargs(x, 0, 0, _("hgignore takes no arguments"))
336 getargs(x, 0, 0, _("hgignore takes no arguments"))
337 return mctx.ctx.repo().dirstate._ignore
337 return mctx.ctx.repo().dirstate._ignore
338
338
339 @predicate('portable()')
339 @predicate('portable()')
340 def portable(mctx, x):
340 def portable(mctx, x):
341 """File that has a portable name. (This doesn't include filenames with case
341 """File that has a portable name. (This doesn't include filenames with case
342 collisions.)
342 collisions.)
343 """
343 """
344 # i18n: "portable" is a keyword
344 # i18n: "portable" is a keyword
345 getargs(x, 0, 0, _("portable takes no arguments"))
345 getargs(x, 0, 0, _("portable takes no arguments"))
346 return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
346 return mctx.predicate(lambda f: util.checkwinfilename(f) is None,
347 predrepr='portable')
347 predrepr='portable')
348
348
349 @predicate('grep(regex)')
349 @predicate('grep(regex)')
350 def grep(mctx, x):
350 def grep(mctx, x):
351 """File contains the given regular expression.
351 """File contains the given regular expression.
352 """
352 """
353 try:
353 try:
354 # i18n: "grep" is a keyword
354 # i18n: "grep" is a keyword
355 r = re.compile(getstring(x, _("grep requires a pattern")))
355 r = re.compile(getstring(x, _("grep requires a pattern")))
356 except re.error as e:
356 except re.error as e:
357 raise error.ParseError(_('invalid match pattern: %s') %
357 raise error.ParseError(_('invalid match pattern: %s') %
358 stringutil.forcebytestr(e))
358 stringutil.forcebytestr(e))
359 return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
359 return mctx.fpredicate(lambda fctx: r.search(fctx.data()),
360 predrepr=('grep(%r)', r.pattern), cache=True)
360 predrepr=('grep(%r)', r.pattern), cache=True)
361
361
362 def _sizetomax(s):
362 def _sizetomax(s):
363 try:
363 try:
364 s = s.strip().lower()
364 s = s.strip().lower()
365 for k, v in util._sizeunits:
365 for k, v in util._sizeunits:
366 if s.endswith(k):
366 if s.endswith(k):
367 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
367 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
368 n = s[:-len(k)]
368 n = s[:-len(k)]
369 inc = 1.0
369 inc = 1.0
370 if "." in n:
370 if "." in n:
371 inc /= 10 ** len(n.split(".")[1])
371 inc /= 10 ** len(n.split(".")[1])
372 return int((float(n) + inc) * v) - 1
372 return int((float(n) + inc) * v) - 1
373 # no extension, this is a precise value
373 # no extension, this is a precise value
374 return int(s)
374 return int(s)
375 except ValueError:
375 except ValueError:
376 raise error.ParseError(_("couldn't parse size: %s") % s)
376 raise error.ParseError(_("couldn't parse size: %s") % s)
377
377
378 def sizematcher(expr):
378 def sizematcher(expr):
379 """Return a function(size) -> bool from the ``size()`` expression"""
379 """Return a function(size) -> bool from the ``size()`` expression"""
380 expr = expr.strip()
380 expr = expr.strip()
381 if '-' in expr: # do we have a range?
381 if '-' in expr: # do we have a range?
382 a, b = expr.split('-', 1)
382 a, b = expr.split('-', 1)
383 a = util.sizetoint(a)
383 a = util.sizetoint(a)
384 b = util.sizetoint(b)
384 b = util.sizetoint(b)
385 return lambda x: x >= a and x <= b
385 return lambda x: x >= a and x <= b
386 elif expr.startswith("<="):
386 elif expr.startswith("<="):
387 a = util.sizetoint(expr[2:])
387 a = util.sizetoint(expr[2:])
388 return lambda x: x <= a
388 return lambda x: x <= a
389 elif expr.startswith("<"):
389 elif expr.startswith("<"):
390 a = util.sizetoint(expr[1:])
390 a = util.sizetoint(expr[1:])
391 return lambda x: x < a
391 return lambda x: x < a
392 elif expr.startswith(">="):
392 elif expr.startswith(">="):
393 a = util.sizetoint(expr[2:])
393 a = util.sizetoint(expr[2:])
394 return lambda x: x >= a
394 return lambda x: x >= a
395 elif expr.startswith(">"):
395 elif expr.startswith(">"):
396 a = util.sizetoint(expr[1:])
396 a = util.sizetoint(expr[1:])
397 return lambda x: x > a
397 return lambda x: x > a
398 else:
398 else:
399 a = util.sizetoint(expr)
399 a = util.sizetoint(expr)
400 b = _sizetomax(expr)
400 b = _sizetomax(expr)
401 return lambda x: x >= a and x <= b
401 return lambda x: x >= a and x <= b
402
402
403 @predicate('size(expression)')
403 @predicate('size(expression)')
404 def size(mctx, x):
404 def size(mctx, x):
405 """File size matches the given expression. Examples:
405 """File size matches the given expression. Examples:
406
406
407 - size('1k') - files from 1024 to 2047 bytes
407 - size('1k') - files from 1024 to 2047 bytes
408 - size('< 20k') - files less than 20480 bytes
408 - size('< 20k') - files less than 20480 bytes
409 - size('>= .5MB') - files at least 524288 bytes
409 - size('>= .5MB') - files at least 524288 bytes
410 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
410 - size('4k - 1MB') - files from 4096 bytes to 1048576 bytes
411 """
411 """
412 # i18n: "size" is a keyword
412 # i18n: "size" is a keyword
413 expr = getstring(x, _("size requires an expression"))
413 expr = getstring(x, _("size requires an expression"))
414 m = sizematcher(expr)
414 m = sizematcher(expr)
415 return mctx.fpredicate(lambda fctx: m(fctx.size()),
415 return mctx.fpredicate(lambda fctx: m(fctx.size()),
416 predrepr=('size(%r)', expr), cache=True)
416 predrepr=('size(%r)', expr), cache=True)
417
417
418 @predicate('encoding(name)')
418 @predicate('encoding(name)')
419 def encoding(mctx, x):
419 def encoding(mctx, x):
420 """File can be successfully decoded with the given character
420 """File can be successfully decoded with the given character
421 encoding. May not be useful for encodings other than ASCII and
421 encoding. May not be useful for encodings other than ASCII and
422 UTF-8.
422 UTF-8.
423 """
423 """
424
424
425 # i18n: "encoding" is a keyword
425 # i18n: "encoding" is a keyword
426 enc = getstring(x, _("encoding requires an encoding name"))
426 enc = getstring(x, _("encoding requires an encoding name"))
427
427
428 def encp(fctx):
428 def encp(fctx):
429 d = fctx.data()
429 d = fctx.data()
430 try:
430 try:
431 d.decode(pycompat.sysstr(enc))
431 d.decode(pycompat.sysstr(enc))
432 return True
432 return True
433 except LookupError:
433 except LookupError:
434 raise error.Abort(_("unknown encoding '%s'") % enc)
434 raise error.Abort(_("unknown encoding '%s'") % enc)
435 except UnicodeDecodeError:
435 except UnicodeDecodeError:
436 return False
436 return False
437
437
438 return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
438 return mctx.fpredicate(encp, predrepr=('encoding(%r)', enc), cache=True)
439
439
440 @predicate('eol(style)')
440 @predicate('eol(style)')
441 def eol(mctx, x):
441 def eol(mctx, x):
442 """File contains newlines of the given style (dos, unix, mac). Binary
442 """File contains newlines of the given style (dos, unix, mac). Binary
443 files are excluded, files with mixed line endings match multiple
443 files are excluded, files with mixed line endings match multiple
444 styles.
444 styles.
445 """
445 """
446
446
447 # i18n: "eol" is a keyword
447 # i18n: "eol" is a keyword
448 enc = getstring(x, _("eol requires a style name"))
448 enc = getstring(x, _("eol requires a style name"))
449
449
450 def eolp(fctx):
450 def eolp(fctx):
451 if fctx.isbinary():
451 if fctx.isbinary():
452 return False
452 return False
453 d = fctx.data()
453 d = fctx.data()
454 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
454 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
455 return True
455 return True
456 elif enc == 'unix' and re.search('(?<!\r)\n', d):
456 elif enc == 'unix' and re.search('(?<!\r)\n', d):
457 return True
457 return True
458 elif enc == 'mac' and re.search('\r(?!\n)', d):
458 elif enc == 'mac' and re.search('\r(?!\n)', d):
459 return True
459 return True
460 return False
460 return False
461 return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
461 return mctx.fpredicate(eolp, predrepr=('eol(%r)', enc), cache=True)
462
462
463 @predicate('copied()')
463 @predicate('copied()')
464 def copied(mctx, x):
464 def copied(mctx, x):
465 """File that is recorded as being copied.
465 """File that is recorded as being copied.
466 """
466 """
467 # i18n: "copied" is a keyword
467 # i18n: "copied" is a keyword
468 getargs(x, 0, 0, _("copied takes no arguments"))
468 getargs(x, 0, 0, _("copied takes no arguments"))
469 def copiedp(fctx):
469 def copiedp(fctx):
470 p = fctx.parents()
470 p = fctx.parents()
471 return p and p[0].path() != fctx.path()
471 return p and p[0].path() != fctx.path()
472 return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
472 return mctx.fpredicate(copiedp, predrepr='copied', cache=True)
473
473
474 @predicate('revs(revs, pattern)')
474 @predicate('revs(revs, pattern)')
475 def revs(mctx, x):
475 def revs(mctx, x):
476 """Evaluate set in the specified revisions. If the revset match multiple
476 """Evaluate set in the specified revisions. If the revset match multiple
477 revs, this will return file matching pattern in any of the revision.
477 revs, this will return file matching pattern in any of the revision.
478 """
478 """
479 # i18n: "revs" is a keyword
479 # i18n: "revs" is a keyword
480 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
480 r, x = getargs(x, 2, 2, _("revs takes two arguments"))
481 # i18n: "revs" is a keyword
481 # i18n: "revs" is a keyword
482 revspec = getstring(r, _("first argument to revs must be a revision"))
482 revspec = getstring(r, _("first argument to revs must be a revision"))
483 repo = mctx.ctx.repo()
483 repo = mctx.ctx.repo()
484 revs = scmutil.revrange(repo, [revspec])
484 revs = scmutil.revrange(repo, [revspec])
485
485
486 matchers = []
486 matchers = []
487 for r in revs:
487 for r in revs:
488 ctx = repo[r]
488 ctx = repo[r]
489 matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x))
489 matchers.append(getmatch(mctx.switch(ctx, _buildstatus(ctx, x)), x))
490 if not matchers:
490 if not matchers:
491 return mctx.never()
491 return mctx.never()
492 if len(matchers) == 1:
492 if len(matchers) == 1:
493 return matchers[0]
493 return matchers[0]
494 return matchmod.unionmatcher(matchers)
494 return matchmod.unionmatcher(matchers)
495
495
496 @predicate('status(base, rev, pattern)')
496 @predicate('status(base, rev, pattern)')
497 def status(mctx, x):
497 def status(mctx, x):
498 """Evaluate predicate using status change between ``base`` and
498 """Evaluate predicate using status change between ``base`` and
499 ``rev``. Examples:
499 ``rev``. Examples:
500
500
501 - ``status(3, 7, added())`` - matches files added from "3" to "7"
501 - ``status(3, 7, added())`` - matches files added from "3" to "7"
502 """
502 """
503 repo = mctx.ctx.repo()
503 repo = mctx.ctx.repo()
504 # i18n: "status" is a keyword
504 # i18n: "status" is a keyword
505 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
505 b, r, x = getargs(x, 3, 3, _("status takes three arguments"))
506 # i18n: "status" is a keyword
506 # i18n: "status" is a keyword
507 baseerr = _("first argument to status must be a revision")
507 baseerr = _("first argument to status must be a revision")
508 baserevspec = getstring(b, baseerr)
508 baserevspec = getstring(b, baseerr)
509 if not baserevspec:
509 if not baserevspec:
510 raise error.ParseError(baseerr)
510 raise error.ParseError(baseerr)
511 reverr = _("second argument to status must be a revision")
511 reverr = _("second argument to status must be a revision")
512 revspec = getstring(r, reverr)
512 revspec = getstring(r, reverr)
513 if not revspec:
513 if not revspec:
514 raise error.ParseError(reverr)
514 raise error.ParseError(reverr)
515 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
515 basectx, ctx = scmutil.revpair(repo, [baserevspec, revspec])
516 return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
516 return getmatch(mctx.switch(ctx, _buildstatus(ctx, x, basectx=basectx)), x)
517
517
518 @predicate('subrepo([pattern])')
518 @predicate('subrepo([pattern])')
519 def subrepo(mctx, x):
519 def subrepo(mctx, x):
520 """Subrepositories whose paths match the given pattern.
520 """Subrepositories whose paths match the given pattern.
521 """
521 """
522 # i18n: "subrepo" is a keyword
522 # i18n: "subrepo" is a keyword
523 getargs(x, 0, 1, _("subrepo takes at most one argument"))
523 getargs(x, 0, 1, _("subrepo takes at most one argument"))
524 ctx = mctx.ctx
524 ctx = mctx.ctx
525 sstate = ctx.substate
525 sstate = ctx.substate
526 if x:
526 if x:
527 pat = getpattern(x, matchmod.allpatternkinds,
527 pat = getpattern(x, matchmod.allpatternkinds,
528 # i18n: "subrepo" is a keyword
528 # i18n: "subrepo" is a keyword
529 _("subrepo requires a pattern or no arguments"))
529 _("subrepo requires a pattern or no arguments"))
530 fast = not matchmod.patkind(pat)
530 fast = not matchmod.patkind(pat)
531 if fast:
531 if fast:
532 def m(s):
532 def m(s):
533 return (s == pat)
533 return (s == pat)
534 else:
534 else:
535 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
535 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
536 return mctx.predicate(lambda f: f in sstate and m(f),
536 return mctx.predicate(lambda f: f in sstate and m(f),
537 predrepr=('subrepo(%r)', pat))
537 predrepr=('subrepo(%r)', pat))
538 else:
538 else:
539 return mctx.predicate(sstate.__contains__, predrepr='subrepo')
539 return mctx.predicate(sstate.__contains__, predrepr='subrepo')
540
540
541 methods = {
541 methods = {
542 'string': stringmatch,
542 'string': stringmatch,
543 'symbol': stringmatch,
543 'symbol': stringmatch,
544 'kindpat': kindpatmatch,
544 'kindpat': kindpatmatch,
545 'and': andmatch,
545 'and': andmatch,
546 'or': ormatch,
546 'or': ormatch,
547 'minus': minusmatch,
547 'minus': minusmatch,
548 'negate': negatematch,
548 'negate': negatematch,
549 'list': listmatch,
549 'list': listmatch,
550 'group': getmatch,
550 'group': getmatch,
551 'not': notmatch,
551 'not': notmatch,
552 'func': func,
552 'func': func,
553 }
553 }
554
554
555 class matchctx(object):
555 class matchctx(object):
556 def __init__(self, ctx, status=None, badfn=None):
556 def __init__(self, ctx, status=None, badfn=None):
557 self.ctx = ctx
557 self.ctx = ctx
558 self._status = status
558 self._status = status
559 self._badfn = badfn
559 self._badfn = badfn
560
560
561 def status(self):
561 def status(self):
562 return self._status
562 return self._status
563
563
564 def matcher(self, patterns):
564 def matcher(self, patterns):
565 return self.ctx.match(patterns, badfn=self._badfn)
565 return self.ctx.match(patterns, badfn=self._badfn)
566
566
567 def predicate(self, predfn, predrepr=None, cache=False):
567 def predicate(self, predfn, predrepr=None, cache=False):
568 """Create a matcher to select files by predfn(filename)"""
568 """Create a matcher to select files by predfn(filename)"""
569 if cache:
569 if cache:
570 predfn = util.cachefunc(predfn)
570 predfn = util.cachefunc(predfn)
571 repo = self.ctx.repo()
571 repo = self.ctx.repo()
572 return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn,
572 return matchmod.predicatematcher(repo.root, repo.getcwd(), predfn,
573 predrepr=predrepr, badfn=self._badfn)
573 predrepr=predrepr, badfn=self._badfn)
574
574
575 def fpredicate(self, predfn, predrepr=None, cache=False):
575 def fpredicate(self, predfn, predrepr=None, cache=False):
576 """Create a matcher to select files by predfn(fctx) at the current
576 """Create a matcher to select files by predfn(fctx) at the current
577 revision
577 revision
578
578
579 Missing files are ignored.
579 Missing files are ignored.
580 """
580 """
581 ctx = self.ctx
581 ctx = self.ctx
582 if ctx.rev() is None:
582 if ctx.rev() is None:
583 def fctxpredfn(f):
583 def fctxpredfn(f):
584 try:
584 try:
585 fctx = ctx[f]
585 fctx = ctx[f]
586 except error.LookupError:
586 except error.LookupError:
587 return False
587 return False
588 try:
588 try:
589 fctx.audit()
589 fctx.audit()
590 except error.Abort:
590 except error.Abort:
591 return False
591 return False
592 try:
592 try:
593 return predfn(fctx)
593 return predfn(fctx)
594 except (IOError, OSError) as e:
594 except (IOError, OSError) as e:
595 # open()-ing a directory fails with EACCES on Windows
595 # open()-ing a directory fails with EACCES on Windows
596 if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR,
596 if e.errno in (errno.ENOENT, errno.EACCES, errno.ENOTDIR,
597 errno.EISDIR):
597 errno.EISDIR):
598 return False
598 return False
599 raise
599 raise
600 else:
600 else:
601 def fctxpredfn(f):
601 def fctxpredfn(f):
602 try:
602 try:
603 fctx = ctx[f]
603 fctx = ctx[f]
604 except error.LookupError:
604 except error.LookupError:
605 return False
605 return False
606 return predfn(fctx)
606 return predfn(fctx)
607 return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
607 return self.predicate(fctxpredfn, predrepr=predrepr, cache=cache)
608
608
609 def never(self):
609 def never(self):
610 """Create a matcher to select nothing"""
610 """Create a matcher to select nothing"""
611 repo = self.ctx.repo()
611 repo = self.ctx.repo()
612 return matchmod.nevermatcher(repo.root, repo.getcwd(),
612 return matchmod.nevermatcher(repo.root, repo.getcwd(),
613 badfn=self._badfn)
613 badfn=self._badfn)
614
614
615 def switch(self, ctx, status=None):
615 def switch(self, ctx, status=None):
616 return matchctx(ctx, status, self._badfn)
616 return matchctx(ctx, status, self._badfn)
617
617
618 # filesets using matchctx.switch()
618 # filesets using matchctx.switch()
619 _switchcallers = [
619 _switchcallers = [
620 'revs',
620 'revs',
621 'status',
621 'status',
622 ]
622 ]
623
623
624 def _intree(funcs, tree):
624 def _intree(funcs, tree):
625 if isinstance(tree, tuple):
625 if isinstance(tree, tuple):
626 if tree[0] == 'func' and tree[1][0] == 'symbol':
626 if tree[0] == 'func' and tree[1][0] == 'symbol':
627 if tree[1][1] in funcs:
627 if tree[1][1] in funcs:
628 return True
628 return True
629 if tree[1][1] in _switchcallers:
629 if tree[1][1] in _switchcallers:
630 # arguments won't be evaluated in the current context
630 # arguments won't be evaluated in the current context
631 return False
631 return False
632 for s in tree[1:]:
632 for s in tree[1:]:
633 if _intree(funcs, s):
633 if _intree(funcs, s):
634 return True
634 return True
635 return False
635 return False
636
636
637 def match(ctx, expr, badfn=None):
637 def match(ctx, expr, badfn=None):
638 """Create a matcher for a single fileset expression"""
638 """Create a matcher for a single fileset expression"""
639 tree = parse(expr)
639 tree = parse(expr)
640 mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
640 mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
641 return getmatch(mctx, tree)
641 return getmatch(mctx, tree)
642
642
643 def _buildstatus(ctx, tree, basectx=None):
643 def _buildstatus(ctx, tree, basectx=None):
644 # do we need status info?
644 # do we need status info?
645
645
646 if _intree(_statuscallers, tree):
646 if _intree(_statuscallers, tree):
647 unknown = _intree(['unknown'], tree)
647 unknown = _intree(['unknown'], tree)
648 ignored = _intree(['ignored'], tree)
648 ignored = _intree(['ignored'], tree)
649
649
650 if basectx is None:
650 if basectx is None:
651 basectx = ctx.p1()
651 basectx = ctx.p1()
652 return basectx.status(ctx, listunknown=unknown, listignored=ignored,
652 return basectx.status(ctx, listunknown=unknown, listignored=ignored,
653 listclean=True)
653 listclean=True)
654 else:
654 else:
655 return None
655 return None
656
656
657 def prettyformat(tree):
657 def prettyformat(tree):
658 return parser.prettyformat(tree, ('string', 'symbol'))
658 return parser.prettyformat(tree, ('string', 'symbol'))
659
659
660 def loadpredicate(ui, extname, registrarobj):
660 def loadpredicate(ui, extname, registrarobj):
661 """Load fileset predicates from specified registrarobj
661 """Load fileset predicates from specified registrarobj
662 """
662 """
663 for name, func in registrarobj._table.iteritems():
663 for name, func in registrarobj._table.iteritems():
664 symbols[name] = func
664 symbols[name] = func
665 if func._callstatus:
665 if func._callstatus:
666 _statuscallers.add(name)
666 _statuscallers.add(name)
667
667
668 # load built-in predicates explicitly to setup _statuscallers
668 # load built-in predicates explicitly to setup _statuscallers
669 loadpredicate(None, None, predicate)
669 loadpredicate(None, None, predicate)
670
670
671 # tell hggettext to extract docstrings from these functions:
671 # tell hggettext to extract docstrings from these functions:
672 i18nfunctions = symbols.values()
672 i18nfunctions = symbols.values()
@@ -1,736 +1,745 b''
1 $ fileset() {
1 $ fileset() {
2 > hg debugfileset --all-files "$@"
2 > hg debugfileset --all-files "$@"
3 > }
3 > }
4
4
5 $ hg init repo
5 $ hg init repo
6 $ cd repo
6 $ cd repo
7 $ echo a > a1
7 $ echo a > a1
8 $ echo a > a2
8 $ echo a > a2
9 $ echo b > b1
9 $ echo b > b1
10 $ echo b > b2
10 $ echo b > b2
11 $ hg ci -Am addfiles
11 $ hg ci -Am addfiles
12 adding a1
12 adding a1
13 adding a2
13 adding a2
14 adding b1
14 adding b1
15 adding b2
15 adding b2
16
16
17 Test operators and basic patterns
17 Test operators and basic patterns
18
18
19 $ fileset -v a1
19 $ fileset -v a1
20 (symbol 'a1')
20 (symbol 'a1')
21 * matcher:
21 * matcher:
22 <patternmatcher patterns='(?:a1$)'>
22 <patternmatcher patterns='(?:a1$)'>
23 a1
23 a1
24 $ fileset -v 'a*'
24 $ fileset -v 'a*'
25 (symbol 'a*')
25 (symbol 'a*')
26 * matcher:
26 * matcher:
27 <patternmatcher patterns='(?:a[^/]*$)'>
27 <patternmatcher patterns='(?:a[^/]*$)'>
28 a1
28 a1
29 a2
29 a2
30 $ fileset -v '"re:a\d"'
30 $ fileset -v '"re:a\d"'
31 (string 're:a\\d')
31 (string 're:a\\d')
32 * matcher:
32 * matcher:
33 <patternmatcher patterns='(?:a\\d)'>
33 <patternmatcher patterns='(?:a\\d)'>
34 a1
34 a1
35 a2
35 a2
36 $ fileset -v '!re:"a\d"'
36 $ fileset -v '!re:"a\d"'
37 (not
37 (not
38 (kindpat
38 (kindpat
39 (symbol 're')
39 (symbol 're')
40 (string 'a\\d')))
40 (string 'a\\d')))
41 * matcher:
41 * matcher:
42 <predicatenmatcher
42 <predicatenmatcher
43 pred=<not
43 pred=<not
44 <patternmatcher patterns='(?:a\\d)'>>>
44 <patternmatcher patterns='(?:a\\d)'>>>
45 b1
45 b1
46 b2
46 b2
47 $ fileset -v 'path:a1 or glob:b?'
47 $ fileset -v 'path:a1 or glob:b?'
48 (or
48 (or
49 (kindpat
49 (kindpat
50 (symbol 'path')
50 (symbol 'path')
51 (symbol 'a1'))
51 (symbol 'a1'))
52 (kindpat
52 (kindpat
53 (symbol 'glob')
53 (symbol 'glob')
54 (symbol 'b?')))
54 (symbol 'b?')))
55 * matcher:
55 * matcher:
56 <unionmatcher matchers=[
56 <unionmatcher matchers=[
57 <patternmatcher patterns='(?:a1(?:/|$))'>,
57 <patternmatcher patterns='(?:a1(?:/|$))'>,
58 <patternmatcher patterns='(?:b.$)'>]>
58 <patternmatcher patterns='(?:b.$)'>]>
59 a1
59 a1
60 b1
60 b1
61 b2
61 b2
62 $ fileset -v --no-show-matcher 'a1 or a2'
62 $ fileset -v --no-show-matcher 'a1 or a2'
63 (or
63 (or
64 (symbol 'a1')
64 (symbol 'a1')
65 (symbol 'a2'))
65 (symbol 'a2'))
66 a1
66 a1
67 a2
67 a2
68 $ fileset 'a1 | a2'
68 $ fileset 'a1 | a2'
69 a1
69 a1
70 a2
70 a2
71 $ fileset 'a* and "*1"'
71 $ fileset 'a* and "*1"'
72 a1
72 a1
73 $ fileset 'a* & "*1"'
73 $ fileset 'a* & "*1"'
74 a1
74 a1
75 $ fileset 'not (r"a*")'
75 $ fileset 'not (r"a*")'
76 b1
76 b1
77 b2
77 b2
78 $ fileset '! ("a*")'
78 $ fileset '! ("a*")'
79 b1
79 b1
80 b2
80 b2
81 $ fileset 'a* - a1'
81 $ fileset 'a* - a1'
82 a2
82 a2
83 $ fileset 'a_b'
83 $ fileset 'a_b'
84 $ fileset '"\xy"'
84 $ fileset '"\xy"'
85 hg: parse error: invalid \x escape* (glob)
85 hg: parse error: invalid \x escape* (glob)
86 [255]
86 [255]
87
87
88 Test invalid syntax
88 Test invalid syntax
89
89
90 $ fileset -v '"added"()'
90 $ fileset -v '"added"()'
91 (func
91 (func
92 (string 'added')
92 (string 'added')
93 None)
93 None)
94 hg: parse error: not a symbol
94 hg: parse error: not a symbol
95 [255]
95 [255]
96 $ fileset -v '()()'
96 $ fileset -v '()()'
97 (func
97 (func
98 (group
98 (group
99 None)
99 None)
100 None)
100 None)
101 hg: parse error: not a symbol
101 hg: parse error: not a symbol
102 [255]
102 [255]
103 $ fileset -v -- '-x'
103 $ fileset -v -- '-x'
104 (negate
104 (negate
105 (symbol 'x'))
105 (symbol 'x'))
106 hg: parse error: can't use negate operator in this context
106 hg: parse error: can't use negate operator in this context
107 [255]
107 [255]
108 $ fileset -v -- '-()'
108 $ fileset -v -- '-()'
109 (negate
109 (negate
110 (group
110 (group
111 None))
111 None))
112 hg: parse error: can't use negate operator in this context
112 hg: parse error: can't use negate operator in this context
113 [255]
113 [255]
114 $ fileset -p parsed 'a, b, c'
115 * parsed:
116 (list
117 (symbol 'a')
118 (symbol 'b')
119 (symbol 'c'))
120 hg: parse error: can't use a list in this context
121 (see hg help "filesets.x or y")
122 [255]
114
123
115 $ fileset '"path":.'
124 $ fileset '"path":.'
116 hg: parse error: not a symbol
125 hg: parse error: not a symbol
117 [255]
126 [255]
118 $ fileset 'path:foo bar'
127 $ fileset 'path:foo bar'
119 hg: parse error at 9: invalid token
128 hg: parse error at 9: invalid token
120 [255]
129 [255]
121 $ fileset 'foo:bar:baz'
130 $ fileset 'foo:bar:baz'
122 hg: parse error: not a symbol
131 hg: parse error: not a symbol
123 [255]
132 [255]
124 $ fileset 'foo:bar()'
133 $ fileset 'foo:bar()'
125 hg: parse error: pattern must be a string
134 hg: parse error: pattern must be a string
126 [255]
135 [255]
127 $ fileset 'foo:bar'
136 $ fileset 'foo:bar'
128 hg: parse error: invalid pattern kind: foo
137 hg: parse error: invalid pattern kind: foo
129 [255]
138 [255]
130
139
131 Show parsed tree at stages:
140 Show parsed tree at stages:
132
141
133 $ fileset -p unknown a
142 $ fileset -p unknown a
134 abort: invalid stage name: unknown
143 abort: invalid stage name: unknown
135 [255]
144 [255]
136
145
137 $ fileset -p parsed 'path:a1 or glob:b?'
146 $ fileset -p parsed 'path:a1 or glob:b?'
138 * parsed:
147 * parsed:
139 (or
148 (or
140 (kindpat
149 (kindpat
141 (symbol 'path')
150 (symbol 'path')
142 (symbol 'a1'))
151 (symbol 'a1'))
143 (kindpat
152 (kindpat
144 (symbol 'glob')
153 (symbol 'glob')
145 (symbol 'b?')))
154 (symbol 'b?')))
146 a1
155 a1
147 b1
156 b1
148 b2
157 b2
149
158
150 $ fileset -p all -s 'a1 or a2 or (grep("b") & clean())'
159 $ fileset -p all -s 'a1 or a2 or (grep("b") & clean())'
151 * parsed:
160 * parsed:
152 (or
161 (or
153 (or
162 (or
154 (symbol 'a1')
163 (symbol 'a1')
155 (symbol 'a2'))
164 (symbol 'a2'))
156 (group
165 (group
157 (and
166 (and
158 (func
167 (func
159 (symbol 'grep')
168 (symbol 'grep')
160 (string 'b'))
169 (string 'b'))
161 (func
170 (func
162 (symbol 'clean')
171 (symbol 'clean')
163 None))))
172 None))))
164 * matcher:
173 * matcher:
165 <unionmatcher matchers=[
174 <unionmatcher matchers=[
166 <unionmatcher matchers=[
175 <unionmatcher matchers=[
167 <patternmatcher patterns='(?:a1$)'>,
176 <patternmatcher patterns='(?:a1$)'>,
168 <patternmatcher patterns='(?:a2$)'>]>,
177 <patternmatcher patterns='(?:a2$)'>]>,
169 <intersectionmatcher
178 <intersectionmatcher
170 m1=<predicatenmatcher pred=grep('b')>,
179 m1=<predicatenmatcher pred=grep('b')>,
171 m2=<predicatenmatcher pred=clean>>]>
180 m2=<predicatenmatcher pred=clean>>]>
172 a1
181 a1
173 a2
182 a2
174 b1
183 b1
175 b2
184 b2
176
185
177 Test files status
186 Test files status
178
187
179 $ rm a1
188 $ rm a1
180 $ hg rm a2
189 $ hg rm a2
181 $ echo b >> b2
190 $ echo b >> b2
182 $ hg cp b1 c1
191 $ hg cp b1 c1
183 $ echo c > c2
192 $ echo c > c2
184 $ echo c > c3
193 $ echo c > c3
185 $ cat > .hgignore <<EOF
194 $ cat > .hgignore <<EOF
186 > \.hgignore
195 > \.hgignore
187 > 2$
196 > 2$
188 > EOF
197 > EOF
189 $ fileset 'modified()'
198 $ fileset 'modified()'
190 b2
199 b2
191 $ fileset 'added()'
200 $ fileset 'added()'
192 c1
201 c1
193 $ fileset 'removed()'
202 $ fileset 'removed()'
194 a2
203 a2
195 $ fileset 'deleted()'
204 $ fileset 'deleted()'
196 a1
205 a1
197 $ fileset 'missing()'
206 $ fileset 'missing()'
198 a1
207 a1
199 $ fileset 'unknown()'
208 $ fileset 'unknown()'
200 c3
209 c3
201 $ fileset 'ignored()'
210 $ fileset 'ignored()'
202 .hgignore
211 .hgignore
203 c2
212 c2
204 $ fileset 'hgignore()'
213 $ fileset 'hgignore()'
205 .hgignore
214 .hgignore
206 a2
215 a2
207 b2
216 b2
208 c2
217 c2
209 $ fileset 'clean()'
218 $ fileset 'clean()'
210 b1
219 b1
211 $ fileset 'copied()'
220 $ fileset 'copied()'
212 c1
221 c1
213
222
214 Test files status in different revisions
223 Test files status in different revisions
215
224
216 $ hg status -m
225 $ hg status -m
217 M b2
226 M b2
218 $ fileset -r0 'revs("wdir()", modified())' --traceback
227 $ fileset -r0 'revs("wdir()", modified())' --traceback
219 b2
228 b2
220 $ hg status -a
229 $ hg status -a
221 A c1
230 A c1
222 $ fileset -r0 'revs("wdir()", added())'
231 $ fileset -r0 'revs("wdir()", added())'
223 c1
232 c1
224 $ hg status --change 0 -a
233 $ hg status --change 0 -a
225 A a1
234 A a1
226 A a2
235 A a2
227 A b1
236 A b1
228 A b2
237 A b2
229 $ hg status -mru
238 $ hg status -mru
230 M b2
239 M b2
231 R a2
240 R a2
232 ? c3
241 ? c3
233 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
242 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
234 a2
243 a2
235 b2
244 b2
236 $ fileset -r0 'added() or revs("wdir()", added())'
245 $ fileset -r0 'added() or revs("wdir()", added())'
237 a1
246 a1
238 a2
247 a2
239 b1
248 b1
240 b2
249 b2
241 c1
250 c1
242
251
243 Test files properties
252 Test files properties
244
253
245 >>> open('bin', 'wb').write(b'\0a') and None
254 >>> open('bin', 'wb').write(b'\0a') and None
246 $ fileset 'binary()'
255 $ fileset 'binary()'
247 bin
256 bin
248 $ fileset 'binary() and unknown()'
257 $ fileset 'binary() and unknown()'
249 bin
258 bin
250 $ echo '^bin$' >> .hgignore
259 $ echo '^bin$' >> .hgignore
251 $ fileset 'binary() and ignored()'
260 $ fileset 'binary() and ignored()'
252 bin
261 bin
253 $ hg add bin
262 $ hg add bin
254 $ fileset 'binary()'
263 $ fileset 'binary()'
255 bin
264 bin
256
265
257 $ fileset 'grep("b{1}")'
266 $ fileset 'grep("b{1}")'
258 .hgignore
267 .hgignore
259 b1
268 b1
260 b2
269 b2
261 c1
270 c1
262 $ fileset 'grep("missingparens(")'
271 $ fileset 'grep("missingparens(")'
263 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \)).* (re)
272 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \)).* (re)
264 [255]
273 [255]
265
274
266 #if execbit
275 #if execbit
267 $ chmod +x b2
276 $ chmod +x b2
268 $ fileset 'exec()'
277 $ fileset 'exec()'
269 b2
278 b2
270 #endif
279 #endif
271
280
272 #if symlink
281 #if symlink
273 $ ln -s b2 b2link
282 $ ln -s b2 b2link
274 $ fileset 'symlink() and unknown()'
283 $ fileset 'symlink() and unknown()'
275 b2link
284 b2link
276 $ hg add b2link
285 $ hg add b2link
277 #endif
286 #endif
278
287
279 #if no-windows
288 #if no-windows
280 $ echo foo > con.xml
289 $ echo foo > con.xml
281 $ fileset 'not portable()'
290 $ fileset 'not portable()'
282 con.xml
291 con.xml
283 $ hg --config ui.portablefilenames=ignore add con.xml
292 $ hg --config ui.portablefilenames=ignore add con.xml
284 #endif
293 #endif
285
294
286 >>> open('1k', 'wb').write(b' '*1024) and None
295 >>> open('1k', 'wb').write(b' '*1024) and None
287 >>> open('2k', 'wb').write(b' '*2048) and None
296 >>> open('2k', 'wb').write(b' '*2048) and None
288 $ hg add 1k 2k
297 $ hg add 1k 2k
289 $ fileset 'size("bar")'
298 $ fileset 'size("bar")'
290 hg: parse error: couldn't parse size: bar
299 hg: parse error: couldn't parse size: bar
291 [255]
300 [255]
292 $ fileset '(1k, 2k)'
301 $ fileset '(1k, 2k)'
293 hg: parse error: can't use a list in this context
302 hg: parse error: can't use a list in this context
294 (see hg help "filesets.x or y")
303 (see hg help "filesets.x or y")
295 [255]
304 [255]
296 $ fileset 'size(1k)'
305 $ fileset 'size(1k)'
297 1k
306 1k
298 $ fileset '(1k or 2k) and size("< 2k")'
307 $ fileset '(1k or 2k) and size("< 2k")'
299 1k
308 1k
300 $ fileset '(1k or 2k) and size("<=2k")'
309 $ fileset '(1k or 2k) and size("<=2k")'
301 1k
310 1k
302 2k
311 2k
303 $ fileset '(1k or 2k) and size("> 1k")'
312 $ fileset '(1k or 2k) and size("> 1k")'
304 2k
313 2k
305 $ fileset '(1k or 2k) and size(">=1K")'
314 $ fileset '(1k or 2k) and size(">=1K")'
306 1k
315 1k
307 2k
316 2k
308 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
317 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
309 1k
318 1k
310 $ fileset 'size("1M")'
319 $ fileset 'size("1M")'
311 $ fileset 'size("1 GB")'
320 $ fileset 'size("1 GB")'
312
321
313 Test merge states
322 Test merge states
314
323
315 $ hg ci -m manychanges
324 $ hg ci -m manychanges
316 $ hg file -r . 'set:copied() & modified()'
325 $ hg file -r . 'set:copied() & modified()'
317 [1]
326 [1]
318 $ hg up -C 0
327 $ hg up -C 0
319 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
328 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
320 $ echo c >> b2
329 $ echo c >> b2
321 $ hg ci -m diverging b2
330 $ hg ci -m diverging b2
322 created new head
331 created new head
323 $ fileset 'resolved()'
332 $ fileset 'resolved()'
324 $ fileset 'unresolved()'
333 $ fileset 'unresolved()'
325 $ hg merge
334 $ hg merge
326 merging b2
335 merging b2
327 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
336 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
328 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
337 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
329 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
338 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
330 [1]
339 [1]
331 $ fileset 'resolved()'
340 $ fileset 'resolved()'
332 $ fileset 'unresolved()'
341 $ fileset 'unresolved()'
333 b2
342 b2
334 $ echo e > b2
343 $ echo e > b2
335 $ hg resolve -m b2
344 $ hg resolve -m b2
336 (no more unresolved files)
345 (no more unresolved files)
337 $ fileset 'resolved()'
346 $ fileset 'resolved()'
338 b2
347 b2
339 $ fileset 'unresolved()'
348 $ fileset 'unresolved()'
340 $ hg ci -m merge
349 $ hg ci -m merge
341
350
342 Test subrepo predicate
351 Test subrepo predicate
343
352
344 $ hg init sub
353 $ hg init sub
345 $ echo a > sub/suba
354 $ echo a > sub/suba
346 $ hg -R sub add sub/suba
355 $ hg -R sub add sub/suba
347 $ hg -R sub ci -m sub
356 $ hg -R sub ci -m sub
348 $ echo 'sub = sub' > .hgsub
357 $ echo 'sub = sub' > .hgsub
349 $ hg init sub2
358 $ hg init sub2
350 $ echo b > sub2/b
359 $ echo b > sub2/b
351 $ hg -R sub2 ci -Am sub2
360 $ hg -R sub2 ci -Am sub2
352 adding b
361 adding b
353 $ echo 'sub2 = sub2' >> .hgsub
362 $ echo 'sub2 = sub2' >> .hgsub
354 $ fileset 'subrepo()'
363 $ fileset 'subrepo()'
355 $ hg add .hgsub
364 $ hg add .hgsub
356 $ fileset 'subrepo()'
365 $ fileset 'subrepo()'
357 sub
366 sub
358 sub2
367 sub2
359 $ fileset 'subrepo("sub")'
368 $ fileset 'subrepo("sub")'
360 sub
369 sub
361 $ fileset 'subrepo("glob:*")'
370 $ fileset 'subrepo("glob:*")'
362 sub
371 sub
363 sub2
372 sub2
364 $ hg ci -m subrepo
373 $ hg ci -m subrepo
365
374
366 Test that .hgsubstate is updated as appropriate during a conversion. The
375 Test that .hgsubstate is updated as appropriate during a conversion. The
367 saverev property is enough to alter the hashes of the subrepo.
376 saverev property is enough to alter the hashes of the subrepo.
368
377
369 $ hg init ../converted
378 $ hg init ../converted
370 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
379 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
371 > sub ../converted/sub
380 > sub ../converted/sub
372 initializing destination ../converted/sub repository
381 initializing destination ../converted/sub repository
373 scanning source...
382 scanning source...
374 sorting...
383 sorting...
375 converting...
384 converting...
376 0 sub
385 0 sub
377 $ hg clone -U sub2 ../converted/sub2
386 $ hg clone -U sub2 ../converted/sub2
378 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
387 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
379 > . ../converted
388 > . ../converted
380 scanning source...
389 scanning source...
381 sorting...
390 sorting...
382 converting...
391 converting...
383 4 addfiles
392 4 addfiles
384 3 manychanges
393 3 manychanges
385 2 diverging
394 2 diverging
386 1 merge
395 1 merge
387 0 subrepo
396 0 subrepo
388 no ".hgsubstate" updates will be made for "sub2"
397 no ".hgsubstate" updates will be made for "sub2"
389 $ hg up -q -R ../converted -r tip
398 $ hg up -q -R ../converted -r tip
390 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
399 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
391 a
400 a
392 b
401 b
393 $ oldnode=`hg log -r tip -T "{node}\n"`
402 $ oldnode=`hg log -r tip -T "{node}\n"`
394 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
403 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
395 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
404 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
396
405
397 Test with a revision
406 Test with a revision
398
407
399 $ hg log -G --template '{rev} {desc}\n'
408 $ hg log -G --template '{rev} {desc}\n'
400 @ 4 subrepo
409 @ 4 subrepo
401 |
410 |
402 o 3 merge
411 o 3 merge
403 |\
412 |\
404 | o 2 diverging
413 | o 2 diverging
405 | |
414 | |
406 o | 1 manychanges
415 o | 1 manychanges
407 |/
416 |/
408 o 0 addfiles
417 o 0 addfiles
409
418
410 $ echo unknown > unknown
419 $ echo unknown > unknown
411 $ fileset -r1 'modified()'
420 $ fileset -r1 'modified()'
412 b2
421 b2
413 $ fileset -r1 'added() and c1'
422 $ fileset -r1 'added() and c1'
414 c1
423 c1
415 $ fileset -r1 'removed()'
424 $ fileset -r1 'removed()'
416 a2
425 a2
417 $ fileset -r1 'deleted()'
426 $ fileset -r1 'deleted()'
418 $ fileset -r1 'unknown()'
427 $ fileset -r1 'unknown()'
419 $ fileset -r1 'ignored()'
428 $ fileset -r1 'ignored()'
420 $ fileset -r1 'hgignore()'
429 $ fileset -r1 'hgignore()'
421 .hgignore
430 .hgignore
422 a2
431 a2
423 b2
432 b2
424 bin
433 bin
425 c2
434 c2
426 sub2
435 sub2
427 $ fileset -r1 'binary()'
436 $ fileset -r1 'binary()'
428 bin
437 bin
429 $ fileset -r1 'size(1k)'
438 $ fileset -r1 'size(1k)'
430 1k
439 1k
431 $ fileset -r3 'resolved()'
440 $ fileset -r3 'resolved()'
432 $ fileset -r3 'unresolved()'
441 $ fileset -r3 'unresolved()'
433
442
434 #if execbit
443 #if execbit
435 $ fileset -r1 'exec()'
444 $ fileset -r1 'exec()'
436 b2
445 b2
437 #endif
446 #endif
438
447
439 #if symlink
448 #if symlink
440 $ fileset -r1 'symlink()'
449 $ fileset -r1 'symlink()'
441 b2link
450 b2link
442 #endif
451 #endif
443
452
444 #if no-windows
453 #if no-windows
445 $ fileset -r1 'not portable()'
454 $ fileset -r1 'not portable()'
446 con.xml
455 con.xml
447 $ hg forget 'con.xml'
456 $ hg forget 'con.xml'
448 #endif
457 #endif
449
458
450 $ fileset -r4 'subrepo("re:su.*")'
459 $ fileset -r4 'subrepo("re:su.*")'
451 sub
460 sub
452 sub2
461 sub2
453 $ fileset -r4 'subrepo(re:su.*)'
462 $ fileset -r4 'subrepo(re:su.*)'
454 sub
463 sub
455 sub2
464 sub2
456 $ fileset -r4 'subrepo("sub")'
465 $ fileset -r4 'subrepo("sub")'
457 sub
466 sub
458 $ fileset -r4 'b2 or c1'
467 $ fileset -r4 'b2 or c1'
459 b2
468 b2
460 c1
469 c1
461
470
462 >>> open('dos', 'wb').write(b"dos\r\n") and None
471 >>> open('dos', 'wb').write(b"dos\r\n") and None
463 >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
472 >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
464 >>> open('mac', 'wb').write(b"mac\r") and None
473 >>> open('mac', 'wb').write(b"mac\r") and None
465 $ hg add dos mixed mac
474 $ hg add dos mixed mac
466
475
467 (remove a1, to examine safety of 'eol' on removed files)
476 (remove a1, to examine safety of 'eol' on removed files)
468 $ rm a1
477 $ rm a1
469
478
470 $ fileset 'eol(dos)'
479 $ fileset 'eol(dos)'
471 dos
480 dos
472 mixed
481 mixed
473 $ fileset 'eol(unix)'
482 $ fileset 'eol(unix)'
474 .hgignore
483 .hgignore
475 .hgsub
484 .hgsub
476 .hgsubstate
485 .hgsubstate
477 b1
486 b1
478 b2
487 b2
479 b2.orig
488 b2.orig
480 c1
489 c1
481 c2
490 c2
482 c3
491 c3
483 con.xml (no-windows !)
492 con.xml (no-windows !)
484 mixed
493 mixed
485 unknown
494 unknown
486 $ fileset 'eol(mac)'
495 $ fileset 'eol(mac)'
487 mac
496 mac
488
497
489 Test safety of 'encoding' on removed files
498 Test safety of 'encoding' on removed files
490
499
491 $ fileset 'encoding("ascii")'
500 $ fileset 'encoding("ascii")'
492 .hgignore
501 .hgignore
493 .hgsub
502 .hgsub
494 .hgsubstate
503 .hgsubstate
495 1k
504 1k
496 2k
505 2k
497 b1
506 b1
498 b2
507 b2
499 b2.orig
508 b2.orig
500 b2link (symlink !)
509 b2link (symlink !)
501 bin
510 bin
502 c1
511 c1
503 c2
512 c2
504 c3
513 c3
505 con.xml (no-windows !)
514 con.xml (no-windows !)
506 dos
515 dos
507 mac
516 mac
508 mixed
517 mixed
509 unknown
518 unknown
510
519
511 Test 'revs(...)'
520 Test 'revs(...)'
512 ================
521 ================
513
522
514 small reminder of the repository state
523 small reminder of the repository state
515
524
516 $ hg log -G
525 $ hg log -G
517 @ changeset: 4:* (glob)
526 @ changeset: 4:* (glob)
518 | tag: tip
527 | tag: tip
519 | user: test
528 | user: test
520 | date: Thu Jan 01 00:00:00 1970 +0000
529 | date: Thu Jan 01 00:00:00 1970 +0000
521 | summary: subrepo
530 | summary: subrepo
522 |
531 |
523 o changeset: 3:* (glob)
532 o changeset: 3:* (glob)
524 |\ parent: 2:55b05bdebf36
533 |\ parent: 2:55b05bdebf36
525 | | parent: 1:* (glob)
534 | | parent: 1:* (glob)
526 | | user: test
535 | | user: test
527 | | date: Thu Jan 01 00:00:00 1970 +0000
536 | | date: Thu Jan 01 00:00:00 1970 +0000
528 | | summary: merge
537 | | summary: merge
529 | |
538 | |
530 | o changeset: 2:55b05bdebf36
539 | o changeset: 2:55b05bdebf36
531 | | parent: 0:8a9576c51c1f
540 | | parent: 0:8a9576c51c1f
532 | | user: test
541 | | user: test
533 | | date: Thu Jan 01 00:00:00 1970 +0000
542 | | date: Thu Jan 01 00:00:00 1970 +0000
534 | | summary: diverging
543 | | summary: diverging
535 | |
544 | |
536 o | changeset: 1:* (glob)
545 o | changeset: 1:* (glob)
537 |/ user: test
546 |/ user: test
538 | date: Thu Jan 01 00:00:00 1970 +0000
547 | date: Thu Jan 01 00:00:00 1970 +0000
539 | summary: manychanges
548 | summary: manychanges
540 |
549 |
541 o changeset: 0:8a9576c51c1f
550 o changeset: 0:8a9576c51c1f
542 user: test
551 user: test
543 date: Thu Jan 01 00:00:00 1970 +0000
552 date: Thu Jan 01 00:00:00 1970 +0000
544 summary: addfiles
553 summary: addfiles
545
554
546 $ hg status --change 0
555 $ hg status --change 0
547 A a1
556 A a1
548 A a2
557 A a2
549 A b1
558 A b1
550 A b2
559 A b2
551 $ hg status --change 1
560 $ hg status --change 1
552 M b2
561 M b2
553 A 1k
562 A 1k
554 A 2k
563 A 2k
555 A b2link (no-windows !)
564 A b2link (no-windows !)
556 A bin
565 A bin
557 A c1
566 A c1
558 A con.xml (no-windows !)
567 A con.xml (no-windows !)
559 R a2
568 R a2
560 $ hg status --change 2
569 $ hg status --change 2
561 M b2
570 M b2
562 $ hg status --change 3
571 $ hg status --change 3
563 M b2
572 M b2
564 A 1k
573 A 1k
565 A 2k
574 A 2k
566 A b2link (no-windows !)
575 A b2link (no-windows !)
567 A bin
576 A bin
568 A c1
577 A c1
569 A con.xml (no-windows !)
578 A con.xml (no-windows !)
570 R a2
579 R a2
571 $ hg status --change 4
580 $ hg status --change 4
572 A .hgsub
581 A .hgsub
573 A .hgsubstate
582 A .hgsubstate
574 $ hg status
583 $ hg status
575 A dos
584 A dos
576 A mac
585 A mac
577 A mixed
586 A mixed
578 R con.xml (no-windows !)
587 R con.xml (no-windows !)
579 ! a1
588 ! a1
580 ? b2.orig
589 ? b2.orig
581 ? c3
590 ? c3
582 ? unknown
591 ? unknown
583
592
584 Test files at -r0 should be filtered by files at wdir
593 Test files at -r0 should be filtered by files at wdir
585 -----------------------------------------------------
594 -----------------------------------------------------
586
595
587 $ fileset -r0 'tracked() and revs("wdir()", tracked())'
596 $ fileset -r0 'tracked() and revs("wdir()", tracked())'
588 a1
597 a1
589 b1
598 b1
590 b2
599 b2
591
600
592 Test that "revs()" work at all
601 Test that "revs()" work at all
593 ------------------------------
602 ------------------------------
594
603
595 $ fileset "revs('2', modified())"
604 $ fileset "revs('2', modified())"
596 b2
605 b2
597
606
598 Test that "revs()" work for file missing in the working copy/current context
607 Test that "revs()" work for file missing in the working copy/current context
599 ----------------------------------------------------------------------------
608 ----------------------------------------------------------------------------
600
609
601 (a2 not in working copy)
610 (a2 not in working copy)
602
611
603 $ fileset "revs('0', added())"
612 $ fileset "revs('0', added())"
604 a1
613 a1
605 a2
614 a2
606 b1
615 b1
607 b2
616 b2
608
617
609 (none of the file exist in "0")
618 (none of the file exist in "0")
610
619
611 $ fileset -r 0 "revs('4', added())"
620 $ fileset -r 0 "revs('4', added())"
612 .hgsub
621 .hgsub
613 .hgsubstate
622 .hgsubstate
614
623
615 Call with empty revset
624 Call with empty revset
616 --------------------------
625 --------------------------
617
626
618 $ fileset "revs('2-2', modified())"
627 $ fileset "revs('2-2', modified())"
619
628
620 Call with revset matching multiple revs
629 Call with revset matching multiple revs
621 ---------------------------------------
630 ---------------------------------------
622
631
623 $ fileset "revs('0+4', added())"
632 $ fileset "revs('0+4', added())"
624 .hgsub
633 .hgsub
625 .hgsubstate
634 .hgsubstate
626 a1
635 a1
627 a2
636 a2
628 b1
637 b1
629 b2
638 b2
630
639
631 overlapping set
640 overlapping set
632
641
633 $ fileset "revs('1+2', modified())"
642 $ fileset "revs('1+2', modified())"
634 b2
643 b2
635
644
636 test 'status(...)'
645 test 'status(...)'
637 =================
646 =================
638
647
639 Simple case
648 Simple case
640 -----------
649 -----------
641
650
642 $ fileset "status(3, 4, added())"
651 $ fileset "status(3, 4, added())"
643 .hgsub
652 .hgsub
644 .hgsubstate
653 .hgsubstate
645
654
646 use rev to restrict matched file
655 use rev to restrict matched file
647 -----------------------------------------
656 -----------------------------------------
648
657
649 $ hg status --removed --rev 0 --rev 1
658 $ hg status --removed --rev 0 --rev 1
650 R a2
659 R a2
651 $ fileset "status(0, 1, removed())"
660 $ fileset "status(0, 1, removed())"
652 a2
661 a2
653 $ fileset "tracked() and status(0, 1, removed())"
662 $ fileset "tracked() and status(0, 1, removed())"
654 $ fileset -r 4 "status(0, 1, removed())"
663 $ fileset -r 4 "status(0, 1, removed())"
655 a2
664 a2
656 $ fileset -r 4 "tracked() and status(0, 1, removed())"
665 $ fileset -r 4 "tracked() and status(0, 1, removed())"
657 $ fileset "revs('4', tracked() and status(0, 1, removed()))"
666 $ fileset "revs('4', tracked() and status(0, 1, removed()))"
658 $ fileset "revs('0', tracked() and status(0, 1, removed()))"
667 $ fileset "revs('0', tracked() and status(0, 1, removed()))"
659 a2
668 a2
660
669
661 check wdir()
670 check wdir()
662 ------------
671 ------------
663
672
664 $ hg status --removed --rev 4
673 $ hg status --removed --rev 4
665 R con.xml (no-windows !)
674 R con.xml (no-windows !)
666 $ fileset "status(4, 'wdir()', removed())"
675 $ fileset "status(4, 'wdir()', removed())"
667 con.xml (no-windows !)
676 con.xml (no-windows !)
668
677
669 $ hg status --removed --rev 2
678 $ hg status --removed --rev 2
670 R a2
679 R a2
671 $ fileset "status('2', 'wdir()', removed())"
680 $ fileset "status('2', 'wdir()', removed())"
672 a2
681 a2
673
682
674 test backward status
683 test backward status
675 --------------------
684 --------------------
676
685
677 $ hg status --removed --rev 0 --rev 4
686 $ hg status --removed --rev 0 --rev 4
678 R a2
687 R a2
679 $ hg status --added --rev 4 --rev 0
688 $ hg status --added --rev 4 --rev 0
680 A a2
689 A a2
681 $ fileset "status(4, 0, added())"
690 $ fileset "status(4, 0, added())"
682 a2
691 a2
683
692
684 test cross branch status
693 test cross branch status
685 ------------------------
694 ------------------------
686
695
687 $ hg status --added --rev 1 --rev 2
696 $ hg status --added --rev 1 --rev 2
688 A a2
697 A a2
689 $ fileset "status(1, 2, added())"
698 $ fileset "status(1, 2, added())"
690 a2
699 a2
691
700
692 test with multi revs revset
701 test with multi revs revset
693 ---------------------------
702 ---------------------------
694 $ hg status --added --rev 0:1 --rev 3:4
703 $ hg status --added --rev 0:1 --rev 3:4
695 A .hgsub
704 A .hgsub
696 A .hgsubstate
705 A .hgsubstate
697 A 1k
706 A 1k
698 A 2k
707 A 2k
699 A b2link (no-windows !)
708 A b2link (no-windows !)
700 A bin
709 A bin
701 A c1
710 A c1
702 A con.xml (no-windows !)
711 A con.xml (no-windows !)
703 $ fileset "status('0:1', '3:4', added())"
712 $ fileset "status('0:1', '3:4', added())"
704 .hgsub
713 .hgsub
705 .hgsubstate
714 .hgsubstate
706 1k
715 1k
707 2k
716 2k
708 b2link (no-windows !)
717 b2link (no-windows !)
709 bin
718 bin
710 c1
719 c1
711 con.xml (no-windows !)
720 con.xml (no-windows !)
712
721
713 tests with empty value
722 tests with empty value
714 ----------------------
723 ----------------------
715
724
716 Fully empty revset
725 Fully empty revset
717
726
718 $ fileset "status('', '4', added())"
727 $ fileset "status('', '4', added())"
719 hg: parse error: first argument to status must be a revision
728 hg: parse error: first argument to status must be a revision
720 [255]
729 [255]
721 $ fileset "status('2', '', added())"
730 $ fileset "status('2', '', added())"
722 hg: parse error: second argument to status must be a revision
731 hg: parse error: second argument to status must be a revision
723 [255]
732 [255]
724
733
725 Empty revset will error at the revset layer
734 Empty revset will error at the revset layer
726
735
727 $ fileset "status(' ', '4', added())"
736 $ fileset "status(' ', '4', added())"
728 hg: parse error at 1: not a prefix: end
737 hg: parse error at 1: not a prefix: end
729 (
738 (
730 ^ here)
739 ^ here)
731 [255]
740 [255]
732 $ fileset "status('2', ' ', added())"
741 $ fileset "status('2', ' ', added())"
733 hg: parse error at 1: not a prefix: end
742 hg: parse error at 1: not a prefix: end
734 (
743 (
735 ^ here)
744 ^ here)
736 [255]
745 [255]
General Comments 0
You need to be logged in to leave comments. Login now