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