##// END OF EJS Templates
fileset: handle error of string unescaping
Yuya Nishihara -
r26233:d3dbb65c default
parent child Browse files
Show More
@@ -1,537 +1,537 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 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 merge,
15 merge,
16 parser,
16 parser,
17 util,
17 util,
18 )
18 )
19
19
20 elements = {
20 elements = {
21 # token-type: binding-strength, primary, prefix, infix, suffix
21 # token-type: binding-strength, primary, prefix, infix, suffix
22 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
22 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
23 "-": (5, None, ("negate", 19), ("minus", 5), None),
23 "-": (5, None, ("negate", 19), ("minus", 5), None),
24 "not": (10, None, ("not", 10), None, None),
24 "not": (10, None, ("not", 10), None, None),
25 "!": (10, None, ("not", 10), None, None),
25 "!": (10, None, ("not", 10), None, None),
26 "and": (5, None, None, ("and", 5), None),
26 "and": (5, None, None, ("and", 5), None),
27 "&": (5, None, None, ("and", 5), None),
27 "&": (5, None, None, ("and", 5), None),
28 "or": (4, None, None, ("or", 4), None),
28 "or": (4, None, None, ("or", 4), None),
29 "|": (4, None, None, ("or", 4), None),
29 "|": (4, None, None, ("or", 4), None),
30 "+": (4, None, None, ("or", 4), None),
30 "+": (4, None, None, ("or", 4), None),
31 ",": (2, None, None, ("list", 2), None),
31 ",": (2, None, None, ("list", 2), None),
32 ")": (0, None, None, None, None),
32 ")": (0, None, None, None, None),
33 "symbol": (0, "symbol", None, None, None),
33 "symbol": (0, "symbol", None, None, None),
34 "string": (0, "string", None, None, None),
34 "string": (0, "string", None, None, None),
35 "end": (0, None, None, None, None),
35 "end": (0, None, None, None, None),
36 }
36 }
37
37
38 keywords = set(['and', 'or', 'not'])
38 keywords = set(['and', 'or', 'not'])
39
39
40 globchars = ".*{}[]?/\\_"
40 globchars = ".*{}[]?/\\_"
41
41
42 def tokenize(program):
42 def tokenize(program):
43 pos, l = 0, len(program)
43 pos, l = 0, len(program)
44 while pos < l:
44 while pos < l:
45 c = program[pos]
45 c = program[pos]
46 if c.isspace(): # skip inter-token whitespace
46 if c.isspace(): # skip inter-token whitespace
47 pass
47 pass
48 elif c in "(),-|&+!": # handle simple operators
48 elif c in "(),-|&+!": # handle simple operators
49 yield (c, None, pos)
49 yield (c, None, pos)
50 elif (c in '"\'' or c == 'r' and
50 elif (c in '"\'' or c == 'r' and
51 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
51 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
52 if c == 'r':
52 if c == 'r':
53 pos += 1
53 pos += 1
54 c = program[pos]
54 c = program[pos]
55 decode = lambda x: x
55 decode = lambda x: x
56 else:
56 else:
57 decode = lambda x: x.decode('string-escape')
57 decode = parser.unescapestr
58 pos += 1
58 pos += 1
59 s = pos
59 s = pos
60 while pos < l: # find closing quote
60 while pos < l: # find closing quote
61 d = program[pos]
61 d = program[pos]
62 if d == '\\': # skip over escaped characters
62 if d == '\\': # skip over escaped characters
63 pos += 2
63 pos += 2
64 continue
64 continue
65 if d == c:
65 if d == c:
66 yield ('string', decode(program[s:pos]), s)
66 yield ('string', decode(program[s:pos]), s)
67 break
67 break
68 pos += 1
68 pos += 1
69 else:
69 else:
70 raise error.ParseError(_("unterminated string"), s)
70 raise error.ParseError(_("unterminated string"), s)
71 elif c.isalnum() or c in globchars or ord(c) > 127:
71 elif c.isalnum() or c in globchars or ord(c) > 127:
72 # gather up a symbol/keyword
72 # gather up a symbol/keyword
73 s = pos
73 s = pos
74 pos += 1
74 pos += 1
75 while pos < l: # find end of symbol
75 while pos < l: # find end of symbol
76 d = program[pos]
76 d = program[pos]
77 if not (d.isalnum() or d in globchars or ord(d) > 127):
77 if not (d.isalnum() or d in globchars or ord(d) > 127):
78 break
78 break
79 pos += 1
79 pos += 1
80 sym = program[s:pos]
80 sym = program[s:pos]
81 if sym in keywords: # operator keywords
81 if sym in keywords: # operator keywords
82 yield (sym, None, s)
82 yield (sym, None, s)
83 else:
83 else:
84 yield ('symbol', sym, s)
84 yield ('symbol', sym, s)
85 pos -= 1
85 pos -= 1
86 else:
86 else:
87 raise error.ParseError(_("syntax error"), pos)
87 raise error.ParseError(_("syntax error"), pos)
88 pos += 1
88 pos += 1
89 yield ('end', None, pos)
89 yield ('end', None, pos)
90
90
91 def parse(expr):
91 def parse(expr):
92 p = parser.parser(elements)
92 p = parser.parser(elements)
93 tree, pos = p.parse(tokenize(expr))
93 tree, pos = p.parse(tokenize(expr))
94 if pos != len(expr):
94 if pos != len(expr):
95 raise error.ParseError(_("invalid token"), pos)
95 raise error.ParseError(_("invalid token"), pos)
96 return tree
96 return tree
97
97
98 def getstring(x, err):
98 def getstring(x, err):
99 if x and (x[0] == 'string' or x[0] == 'symbol'):
99 if x and (x[0] == 'string' or x[0] == 'symbol'):
100 return x[1]
100 return x[1]
101 raise error.ParseError(err)
101 raise error.ParseError(err)
102
102
103 def getset(mctx, x):
103 def getset(mctx, x):
104 if not x:
104 if not x:
105 raise error.ParseError(_("missing argument"))
105 raise error.ParseError(_("missing argument"))
106 return methods[x[0]](mctx, *x[1:])
106 return methods[x[0]](mctx, *x[1:])
107
107
108 def stringset(mctx, x):
108 def stringset(mctx, x):
109 m = mctx.matcher([x])
109 m = mctx.matcher([x])
110 return [f for f in mctx.subset if m(f)]
110 return [f for f in mctx.subset if m(f)]
111
111
112 def andset(mctx, x, y):
112 def andset(mctx, x, y):
113 return getset(mctx.narrow(getset(mctx, x)), y)
113 return getset(mctx.narrow(getset(mctx, x)), y)
114
114
115 def orset(mctx, x, y):
115 def orset(mctx, x, y):
116 # needs optimizing
116 # needs optimizing
117 xl = getset(mctx, x)
117 xl = getset(mctx, x)
118 yl = getset(mctx, y)
118 yl = getset(mctx, y)
119 return xl + [f for f in yl if f not in xl]
119 return xl + [f for f in yl if f not in xl]
120
120
121 def notset(mctx, x):
121 def notset(mctx, x):
122 s = set(getset(mctx, x))
122 s = set(getset(mctx, x))
123 return [r for r in mctx.subset if r not in s]
123 return [r for r in mctx.subset if r not in s]
124
124
125 def minusset(mctx, x, y):
125 def minusset(mctx, x, y):
126 xl = getset(mctx, x)
126 xl = getset(mctx, x)
127 yl = set(getset(mctx, y))
127 yl = set(getset(mctx, y))
128 return [f for f in xl if f not in yl]
128 return [f for f in xl if f not in yl]
129
129
130 def listset(mctx, a, b):
130 def listset(mctx, a, b):
131 raise error.ParseError(_("can't use a list in this context"))
131 raise error.ParseError(_("can't use a list in this context"))
132
132
133 def modified(mctx, x):
133 def modified(mctx, x):
134 """``modified()``
134 """``modified()``
135 File that is modified according to :hg:`status`.
135 File that is modified according to :hg:`status`.
136 """
136 """
137 # i18n: "modified" is a keyword
137 # i18n: "modified" is a keyword
138 getargs(x, 0, 0, _("modified takes no arguments"))
138 getargs(x, 0, 0, _("modified takes no arguments"))
139 s = mctx.status().modified
139 s = mctx.status().modified
140 return [f for f in mctx.subset if f in s]
140 return [f for f in mctx.subset if f in s]
141
141
142 def added(mctx, x):
142 def added(mctx, x):
143 """``added()``
143 """``added()``
144 File that is added according to :hg:`status`.
144 File that is added according to :hg:`status`.
145 """
145 """
146 # i18n: "added" is a keyword
146 # i18n: "added" is a keyword
147 getargs(x, 0, 0, _("added takes no arguments"))
147 getargs(x, 0, 0, _("added takes no arguments"))
148 s = mctx.status().added
148 s = mctx.status().added
149 return [f for f in mctx.subset if f in s]
149 return [f for f in mctx.subset if f in s]
150
150
151 def removed(mctx, x):
151 def removed(mctx, x):
152 """``removed()``
152 """``removed()``
153 File that is removed according to :hg:`status`.
153 File that is removed according to :hg:`status`.
154 """
154 """
155 # i18n: "removed" is a keyword
155 # i18n: "removed" is a keyword
156 getargs(x, 0, 0, _("removed takes no arguments"))
156 getargs(x, 0, 0, _("removed takes no arguments"))
157 s = mctx.status().removed
157 s = mctx.status().removed
158 return [f for f in mctx.subset if f in s]
158 return [f for f in mctx.subset if f in s]
159
159
160 def deleted(mctx, x):
160 def deleted(mctx, x):
161 """``deleted()``
161 """``deleted()``
162 File that is deleted according to :hg:`status`.
162 File that is deleted according to :hg:`status`.
163 """
163 """
164 # i18n: "deleted" is a keyword
164 # i18n: "deleted" is a keyword
165 getargs(x, 0, 0, _("deleted takes no arguments"))
165 getargs(x, 0, 0, _("deleted takes no arguments"))
166 s = mctx.status().deleted
166 s = mctx.status().deleted
167 return [f for f in mctx.subset if f in s]
167 return [f for f in mctx.subset if f in s]
168
168
169 def unknown(mctx, x):
169 def unknown(mctx, x):
170 """``unknown()``
170 """``unknown()``
171 File that is unknown according to :hg:`status`. These files will only be
171 File that is unknown according to :hg:`status`. These files will only be
172 considered if this predicate is used.
172 considered if this predicate is used.
173 """
173 """
174 # i18n: "unknown" is a keyword
174 # i18n: "unknown" is a keyword
175 getargs(x, 0, 0, _("unknown takes no arguments"))
175 getargs(x, 0, 0, _("unknown takes no arguments"))
176 s = mctx.status().unknown
176 s = mctx.status().unknown
177 return [f for f in mctx.subset if f in s]
177 return [f for f in mctx.subset if f in s]
178
178
179 def ignored(mctx, x):
179 def ignored(mctx, x):
180 """``ignored()``
180 """``ignored()``
181 File that is ignored according to :hg:`status`. These files will only be
181 File that is ignored according to :hg:`status`. These files will only be
182 considered if this predicate is used.
182 considered if this predicate is used.
183 """
183 """
184 # i18n: "ignored" is a keyword
184 # i18n: "ignored" is a keyword
185 getargs(x, 0, 0, _("ignored takes no arguments"))
185 getargs(x, 0, 0, _("ignored takes no arguments"))
186 s = mctx.status().ignored
186 s = mctx.status().ignored
187 return [f for f in mctx.subset if f in s]
187 return [f for f in mctx.subset if f in s]
188
188
189 def clean(mctx, x):
189 def clean(mctx, x):
190 """``clean()``
190 """``clean()``
191 File that is clean according to :hg:`status`.
191 File that is clean according to :hg:`status`.
192 """
192 """
193 # i18n: "clean" is a keyword
193 # i18n: "clean" is a keyword
194 getargs(x, 0, 0, _("clean takes no arguments"))
194 getargs(x, 0, 0, _("clean takes no arguments"))
195 s = mctx.status().clean
195 s = mctx.status().clean
196 return [f for f in mctx.subset if f in s]
196 return [f for f in mctx.subset if f in s]
197
197
198 def func(mctx, a, b):
198 def func(mctx, a, b):
199 if a[0] == 'symbol' and a[1] in symbols:
199 if a[0] == 'symbol' and a[1] in symbols:
200 return symbols[a[1]](mctx, b)
200 return symbols[a[1]](mctx, b)
201
201
202 keep = lambda fn: getattr(fn, '__doc__', None) is not None
202 keep = lambda fn: getattr(fn, '__doc__', None) is not None
203
203
204 syms = [s for (s, fn) in symbols.items() if keep(fn)]
204 syms = [s for (s, fn) in symbols.items() if keep(fn)]
205 raise error.UnknownIdentifier(a[1], syms)
205 raise error.UnknownIdentifier(a[1], syms)
206
206
207 def getlist(x):
207 def getlist(x):
208 if not x:
208 if not x:
209 return []
209 return []
210 if x[0] == 'list':
210 if x[0] == 'list':
211 return getlist(x[1]) + [x[2]]
211 return getlist(x[1]) + [x[2]]
212 return [x]
212 return [x]
213
213
214 def getargs(x, min, max, err):
214 def getargs(x, min, max, err):
215 l = getlist(x)
215 l = getlist(x)
216 if len(l) < min or len(l) > max:
216 if len(l) < min or len(l) > max:
217 raise error.ParseError(err)
217 raise error.ParseError(err)
218 return l
218 return l
219
219
220 def binary(mctx, x):
220 def binary(mctx, x):
221 """``binary()``
221 """``binary()``
222 File that appears to be binary (contains NUL bytes).
222 File that appears to be binary (contains NUL bytes).
223 """
223 """
224 # i18n: "binary" is a keyword
224 # i18n: "binary" is a keyword
225 getargs(x, 0, 0, _("binary takes no arguments"))
225 getargs(x, 0, 0, _("binary takes no arguments"))
226 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
226 return [f for f in mctx.existing() if util.binary(mctx.ctx[f].data())]
227
227
228 def exec_(mctx, x):
228 def exec_(mctx, x):
229 """``exec()``
229 """``exec()``
230 File that is marked as executable.
230 File that is marked as executable.
231 """
231 """
232 # i18n: "exec" is a keyword
232 # i18n: "exec" is a keyword
233 getargs(x, 0, 0, _("exec takes no arguments"))
233 getargs(x, 0, 0, _("exec takes no arguments"))
234 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
234 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'x']
235
235
236 def symlink(mctx, x):
236 def symlink(mctx, x):
237 """``symlink()``
237 """``symlink()``
238 File that is marked as a symlink.
238 File that is marked as a symlink.
239 """
239 """
240 # i18n: "symlink" is a keyword
240 # i18n: "symlink" is a keyword
241 getargs(x, 0, 0, _("symlink takes no arguments"))
241 getargs(x, 0, 0, _("symlink takes no arguments"))
242 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
242 return [f for f in mctx.existing() if mctx.ctx.flags(f) == 'l']
243
243
244 def resolved(mctx, x):
244 def resolved(mctx, x):
245 """``resolved()``
245 """``resolved()``
246 File that is marked resolved according to :hg:`resolve -l`.
246 File that is marked resolved according to :hg:`resolve -l`.
247 """
247 """
248 # i18n: "resolved" is a keyword
248 # i18n: "resolved" is a keyword
249 getargs(x, 0, 0, _("resolved takes no arguments"))
249 getargs(x, 0, 0, _("resolved takes no arguments"))
250 if mctx.ctx.rev() is not None:
250 if mctx.ctx.rev() is not None:
251 return []
251 return []
252 ms = merge.mergestate(mctx.ctx.repo())
252 ms = merge.mergestate(mctx.ctx.repo())
253 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
253 return [f for f in mctx.subset if f in ms and ms[f] == 'r']
254
254
255 def unresolved(mctx, x):
255 def unresolved(mctx, x):
256 """``unresolved()``
256 """``unresolved()``
257 File that is marked unresolved according to :hg:`resolve -l`.
257 File that is marked unresolved according to :hg:`resolve -l`.
258 """
258 """
259 # i18n: "unresolved" is a keyword
259 # i18n: "unresolved" is a keyword
260 getargs(x, 0, 0, _("unresolved takes no arguments"))
260 getargs(x, 0, 0, _("unresolved takes no arguments"))
261 if mctx.ctx.rev() is not None:
261 if mctx.ctx.rev() is not None:
262 return []
262 return []
263 ms = merge.mergestate(mctx.ctx.repo())
263 ms = merge.mergestate(mctx.ctx.repo())
264 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
264 return [f for f in mctx.subset if f in ms and ms[f] == 'u']
265
265
266 def hgignore(mctx, x):
266 def hgignore(mctx, x):
267 """``hgignore()``
267 """``hgignore()``
268 File that matches the active .hgignore pattern.
268 File that matches the active .hgignore pattern.
269 """
269 """
270 # i18n: "hgignore" is a keyword
270 # i18n: "hgignore" is a keyword
271 getargs(x, 0, 0, _("hgignore takes no arguments"))
271 getargs(x, 0, 0, _("hgignore takes no arguments"))
272 ignore = mctx.ctx.repo().dirstate._ignore
272 ignore = mctx.ctx.repo().dirstate._ignore
273 return [f for f in mctx.subset if ignore(f)]
273 return [f for f in mctx.subset if ignore(f)]
274
274
275 def portable(mctx, x):
275 def portable(mctx, x):
276 """``portable()``
276 """``portable()``
277 File that has a portable name. (This doesn't include filenames with case
277 File that has a portable name. (This doesn't include filenames with case
278 collisions.)
278 collisions.)
279 """
279 """
280 # i18n: "portable" is a keyword
280 # i18n: "portable" is a keyword
281 getargs(x, 0, 0, _("portable takes no arguments"))
281 getargs(x, 0, 0, _("portable takes no arguments"))
282 checkwinfilename = util.checkwinfilename
282 checkwinfilename = util.checkwinfilename
283 return [f for f in mctx.subset if checkwinfilename(f) is None]
283 return [f for f in mctx.subset if checkwinfilename(f) is None]
284
284
285 def grep(mctx, x):
285 def grep(mctx, x):
286 """``grep(regex)``
286 """``grep(regex)``
287 File contains the given regular expression.
287 File contains the given regular expression.
288 """
288 """
289 try:
289 try:
290 # i18n: "grep" is a keyword
290 # i18n: "grep" is a keyword
291 r = re.compile(getstring(x, _("grep requires a pattern")))
291 r = re.compile(getstring(x, _("grep requires a pattern")))
292 except re.error as e:
292 except re.error as e:
293 raise error.ParseError(_('invalid match pattern: %s') % e)
293 raise error.ParseError(_('invalid match pattern: %s') % e)
294 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
294 return [f for f in mctx.existing() if r.search(mctx.ctx[f].data())]
295
295
296 def _sizetomax(s):
296 def _sizetomax(s):
297 try:
297 try:
298 s = s.strip().lower()
298 s = s.strip().lower()
299 for k, v in util._sizeunits:
299 for k, v in util._sizeunits:
300 if s.endswith(k):
300 if s.endswith(k):
301 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
301 # max(4k) = 5k - 1, max(4.5k) = 4.6k - 1
302 n = s[:-len(k)]
302 n = s[:-len(k)]
303 inc = 1.0
303 inc = 1.0
304 if "." in n:
304 if "." in n:
305 inc /= 10 ** len(n.split(".")[1])
305 inc /= 10 ** len(n.split(".")[1])
306 return int((float(n) + inc) * v) - 1
306 return int((float(n) + inc) * v) - 1
307 # no extension, this is a precise value
307 # no extension, this is a precise value
308 return int(s)
308 return int(s)
309 except ValueError:
309 except ValueError:
310 raise error.ParseError(_("couldn't parse size: %s") % s)
310 raise error.ParseError(_("couldn't parse size: %s") % s)
311
311
312 def size(mctx, x):
312 def size(mctx, x):
313 """``size(expression)``
313 """``size(expression)``
314 File size matches the given expression. Examples:
314 File size matches the given expression. Examples:
315
315
316 - 1k (files from 1024 to 2047 bytes)
316 - 1k (files from 1024 to 2047 bytes)
317 - < 20k (files less than 20480 bytes)
317 - < 20k (files less than 20480 bytes)
318 - >= .5MB (files at least 524288 bytes)
318 - >= .5MB (files at least 524288 bytes)
319 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
319 - 4k - 1MB (files from 4096 bytes to 1048576 bytes)
320 """
320 """
321
321
322 # i18n: "size" is a keyword
322 # i18n: "size" is a keyword
323 expr = getstring(x, _("size requires an expression")).strip()
323 expr = getstring(x, _("size requires an expression")).strip()
324 if '-' in expr: # do we have a range?
324 if '-' in expr: # do we have a range?
325 a, b = expr.split('-', 1)
325 a, b = expr.split('-', 1)
326 a = util.sizetoint(a)
326 a = util.sizetoint(a)
327 b = util.sizetoint(b)
327 b = util.sizetoint(b)
328 m = lambda x: x >= a and x <= b
328 m = lambda x: x >= a and x <= b
329 elif expr.startswith("<="):
329 elif expr.startswith("<="):
330 a = util.sizetoint(expr[2:])
330 a = util.sizetoint(expr[2:])
331 m = lambda x: x <= a
331 m = lambda x: x <= a
332 elif expr.startswith("<"):
332 elif expr.startswith("<"):
333 a = util.sizetoint(expr[1:])
333 a = util.sizetoint(expr[1:])
334 m = lambda x: x < a
334 m = lambda x: x < a
335 elif expr.startswith(">="):
335 elif expr.startswith(">="):
336 a = util.sizetoint(expr[2:])
336 a = util.sizetoint(expr[2:])
337 m = lambda x: x >= a
337 m = lambda x: x >= a
338 elif expr.startswith(">"):
338 elif expr.startswith(">"):
339 a = util.sizetoint(expr[1:])
339 a = util.sizetoint(expr[1:])
340 m = lambda x: x > a
340 m = lambda x: x > a
341 elif expr[0].isdigit or expr[0] == '.':
341 elif expr[0].isdigit or expr[0] == '.':
342 a = util.sizetoint(expr)
342 a = util.sizetoint(expr)
343 b = _sizetomax(expr)
343 b = _sizetomax(expr)
344 m = lambda x: x >= a and x <= b
344 m = lambda x: x >= a and x <= b
345 else:
345 else:
346 raise error.ParseError(_("couldn't parse size: %s") % expr)
346 raise error.ParseError(_("couldn't parse size: %s") % expr)
347
347
348 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
348 return [f for f in mctx.existing() if m(mctx.ctx[f].size())]
349
349
350 def encoding(mctx, x):
350 def encoding(mctx, x):
351 """``encoding(name)``
351 """``encoding(name)``
352 File can be successfully decoded with the given character
352 File can be successfully decoded with the given character
353 encoding. May not be useful for encodings other than ASCII and
353 encoding. May not be useful for encodings other than ASCII and
354 UTF-8.
354 UTF-8.
355 """
355 """
356
356
357 # i18n: "encoding" is a keyword
357 # i18n: "encoding" is a keyword
358 enc = getstring(x, _("encoding requires an encoding name"))
358 enc = getstring(x, _("encoding requires an encoding name"))
359
359
360 s = []
360 s = []
361 for f in mctx.existing():
361 for f in mctx.existing():
362 d = mctx.ctx[f].data()
362 d = mctx.ctx[f].data()
363 try:
363 try:
364 d.decode(enc)
364 d.decode(enc)
365 except LookupError:
365 except LookupError:
366 raise util.Abort(_("unknown encoding '%s'") % enc)
366 raise util.Abort(_("unknown encoding '%s'") % enc)
367 except UnicodeDecodeError:
367 except UnicodeDecodeError:
368 continue
368 continue
369 s.append(f)
369 s.append(f)
370
370
371 return s
371 return s
372
372
373 def eol(mctx, x):
373 def eol(mctx, x):
374 """``eol(style)``
374 """``eol(style)``
375 File contains newlines of the given style (dos, unix, mac). Binary
375 File contains newlines of the given style (dos, unix, mac). Binary
376 files are excluded, files with mixed line endings match multiple
376 files are excluded, files with mixed line endings match multiple
377 styles.
377 styles.
378 """
378 """
379
379
380 # i18n: "encoding" is a keyword
380 # i18n: "encoding" is a keyword
381 enc = getstring(x, _("encoding requires an encoding name"))
381 enc = getstring(x, _("encoding requires an encoding name"))
382
382
383 s = []
383 s = []
384 for f in mctx.existing():
384 for f in mctx.existing():
385 d = mctx.ctx[f].data()
385 d = mctx.ctx[f].data()
386 if util.binary(d):
386 if util.binary(d):
387 continue
387 continue
388 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
388 if (enc == 'dos' or enc == 'win') and '\r\n' in d:
389 s.append(f)
389 s.append(f)
390 elif enc == 'unix' and re.search('(?<!\r)\n', d):
390 elif enc == 'unix' and re.search('(?<!\r)\n', d):
391 s.append(f)
391 s.append(f)
392 elif enc == 'mac' and re.search('\r(?!\n)', d):
392 elif enc == 'mac' and re.search('\r(?!\n)', d):
393 s.append(f)
393 s.append(f)
394 return s
394 return s
395
395
396 def copied(mctx, x):
396 def copied(mctx, x):
397 """``copied()``
397 """``copied()``
398 File that is recorded as being copied.
398 File that is recorded as being copied.
399 """
399 """
400 # i18n: "copied" is a keyword
400 # i18n: "copied" is a keyword
401 getargs(x, 0, 0, _("copied takes no arguments"))
401 getargs(x, 0, 0, _("copied takes no arguments"))
402 s = []
402 s = []
403 for f in mctx.subset:
403 for f in mctx.subset:
404 p = mctx.ctx[f].parents()
404 p = mctx.ctx[f].parents()
405 if p and p[0].path() != f:
405 if p and p[0].path() != f:
406 s.append(f)
406 s.append(f)
407 return s
407 return s
408
408
409 def subrepo(mctx, x):
409 def subrepo(mctx, x):
410 """``subrepo([pattern])``
410 """``subrepo([pattern])``
411 Subrepositories whose paths match the given pattern.
411 Subrepositories whose paths match the given pattern.
412 """
412 """
413 # i18n: "subrepo" is a keyword
413 # i18n: "subrepo" is a keyword
414 getargs(x, 0, 1, _("subrepo takes at most one argument"))
414 getargs(x, 0, 1, _("subrepo takes at most one argument"))
415 ctx = mctx.ctx
415 ctx = mctx.ctx
416 sstate = sorted(ctx.substate)
416 sstate = sorted(ctx.substate)
417 if x:
417 if x:
418 # i18n: "subrepo" is a keyword
418 # i18n: "subrepo" is a keyword
419 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
419 pat = getstring(x, _("subrepo requires a pattern or no arguments"))
420
420
421 from . import match as matchmod # avoid circular import issues
421 from . import match as matchmod # avoid circular import issues
422 fast = not matchmod.patkind(pat)
422 fast = not matchmod.patkind(pat)
423 if fast:
423 if fast:
424 def m(s):
424 def m(s):
425 return (s == pat)
425 return (s == pat)
426 else:
426 else:
427 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
427 m = matchmod.match(ctx.repo().root, '', [pat], ctx=ctx)
428 return [sub for sub in sstate if m(sub)]
428 return [sub for sub in sstate if m(sub)]
429 else:
429 else:
430 return [sub for sub in sstate]
430 return [sub for sub in sstate]
431
431
432 symbols = {
432 symbols = {
433 'added': added,
433 'added': added,
434 'binary': binary,
434 'binary': binary,
435 'clean': clean,
435 'clean': clean,
436 'copied': copied,
436 'copied': copied,
437 'deleted': deleted,
437 'deleted': deleted,
438 'encoding': encoding,
438 'encoding': encoding,
439 'eol': eol,
439 'eol': eol,
440 'exec': exec_,
440 'exec': exec_,
441 'grep': grep,
441 'grep': grep,
442 'ignored': ignored,
442 'ignored': ignored,
443 'hgignore': hgignore,
443 'hgignore': hgignore,
444 'modified': modified,
444 'modified': modified,
445 'portable': portable,
445 'portable': portable,
446 'removed': removed,
446 'removed': removed,
447 'resolved': resolved,
447 'resolved': resolved,
448 'size': size,
448 'size': size,
449 'symlink': symlink,
449 'symlink': symlink,
450 'unknown': unknown,
450 'unknown': unknown,
451 'unresolved': unresolved,
451 'unresolved': unresolved,
452 'subrepo': subrepo,
452 'subrepo': subrepo,
453 }
453 }
454
454
455 methods = {
455 methods = {
456 'string': stringset,
456 'string': stringset,
457 'symbol': stringset,
457 'symbol': stringset,
458 'and': andset,
458 'and': andset,
459 'or': orset,
459 'or': orset,
460 'minus': minusset,
460 'minus': minusset,
461 'list': listset,
461 'list': listset,
462 'group': getset,
462 'group': getset,
463 'not': notset,
463 'not': notset,
464 'func': func,
464 'func': func,
465 }
465 }
466
466
467 class matchctx(object):
467 class matchctx(object):
468 def __init__(self, ctx, subset=None, status=None):
468 def __init__(self, ctx, subset=None, status=None):
469 self.ctx = ctx
469 self.ctx = ctx
470 self.subset = subset
470 self.subset = subset
471 self._status = status
471 self._status = status
472 def status(self):
472 def status(self):
473 return self._status
473 return self._status
474 def matcher(self, patterns):
474 def matcher(self, patterns):
475 return self.ctx.match(patterns)
475 return self.ctx.match(patterns)
476 def filter(self, files):
476 def filter(self, files):
477 return [f for f in files if f in self.subset]
477 return [f for f in files if f in self.subset]
478 def existing(self):
478 def existing(self):
479 if self._status is not None:
479 if self._status is not None:
480 removed = set(self._status[3])
480 removed = set(self._status[3])
481 unknown = set(self._status[4] + self._status[5])
481 unknown = set(self._status[4] + self._status[5])
482 else:
482 else:
483 removed = set()
483 removed = set()
484 unknown = set()
484 unknown = set()
485 return (f for f in self.subset
485 return (f for f in self.subset
486 if (f in self.ctx and f not in removed) or f in unknown)
486 if (f in self.ctx and f not in removed) or f in unknown)
487 def narrow(self, files):
487 def narrow(self, files):
488 return matchctx(self.ctx, self.filter(files), self._status)
488 return matchctx(self.ctx, self.filter(files), self._status)
489
489
490 def _intree(funcs, tree):
490 def _intree(funcs, tree):
491 if isinstance(tree, tuple):
491 if isinstance(tree, tuple):
492 if tree[0] == 'func' and tree[1][0] == 'symbol':
492 if tree[0] == 'func' and tree[1][0] == 'symbol':
493 if tree[1][1] in funcs:
493 if tree[1][1] in funcs:
494 return True
494 return True
495 for s in tree[1:]:
495 for s in tree[1:]:
496 if _intree(funcs, s):
496 if _intree(funcs, s):
497 return True
497 return True
498 return False
498 return False
499
499
500 # filesets using matchctx.existing()
500 # filesets using matchctx.existing()
501 _existingcallers = [
501 _existingcallers = [
502 'binary',
502 'binary',
503 'exec',
503 'exec',
504 'grep',
504 'grep',
505 'size',
505 'size',
506 'symlink',
506 'symlink',
507 ]
507 ]
508
508
509 def getfileset(ctx, expr):
509 def getfileset(ctx, expr):
510 tree = parse(expr)
510 tree = parse(expr)
511
511
512 # do we need status info?
512 # do we need status info?
513 if (_intree(['modified', 'added', 'removed', 'deleted',
513 if (_intree(['modified', 'added', 'removed', 'deleted',
514 'unknown', 'ignored', 'clean'], tree) or
514 'unknown', 'ignored', 'clean'], tree) or
515 # Using matchctx.existing() on a workingctx requires us to check
515 # Using matchctx.existing() on a workingctx requires us to check
516 # for deleted files.
516 # for deleted files.
517 (ctx.rev() is None and _intree(_existingcallers, tree))):
517 (ctx.rev() is None and _intree(_existingcallers, tree))):
518 unknown = _intree(['unknown'], tree)
518 unknown = _intree(['unknown'], tree)
519 ignored = _intree(['ignored'], tree)
519 ignored = _intree(['ignored'], tree)
520
520
521 r = ctx.repo()
521 r = ctx.repo()
522 status = r.status(ctx.p1(), ctx,
522 status = r.status(ctx.p1(), ctx,
523 unknown=unknown, ignored=ignored, clean=True)
523 unknown=unknown, ignored=ignored, clean=True)
524 subset = []
524 subset = []
525 for c in status:
525 for c in status:
526 subset.extend(c)
526 subset.extend(c)
527 else:
527 else:
528 status = None
528 status = None
529 subset = list(ctx.walk(ctx.match([])))
529 subset = list(ctx.walk(ctx.match([])))
530
530
531 return getset(matchctx(ctx, subset, status), tree)
531 return getset(matchctx(ctx, subset, status), tree)
532
532
533 def prettyformat(tree):
533 def prettyformat(tree):
534 return parser.prettyformat(tree, ('string', 'symbol'))
534 return parser.prettyformat(tree, ('string', 'symbol'))
535
535
536 # tell hggettext to extract docstrings from these functions:
536 # tell hggettext to extract docstrings from these functions:
537 i18nfunctions = symbols.values()
537 i18nfunctions = symbols.values()
@@ -1,308 +1,311 b''
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 'a1 or a2'
30 $ fileset -v 'a1 or a2'
31 (or
31 (or
32 ('symbol', 'a1')
32 ('symbol', 'a1')
33 ('symbol', 'a2'))
33 ('symbol', 'a2'))
34 a1
34 a1
35 a2
35 a2
36 $ fileset 'a1 | a2'
36 $ fileset 'a1 | a2'
37 a1
37 a1
38 a2
38 a2
39 $ fileset 'a* and "*1"'
39 $ fileset 'a* and "*1"'
40 a1
40 a1
41 $ fileset 'a* & "*1"'
41 $ fileset 'a* & "*1"'
42 a1
42 a1
43 $ fileset 'not (r"a*")'
43 $ fileset 'not (r"a*")'
44 b1
44 b1
45 b2
45 b2
46 $ fileset '! ("a*")'
46 $ fileset '! ("a*")'
47 b1
47 b1
48 b2
48 b2
49 $ fileset 'a* - a1'
49 $ fileset 'a* - a1'
50 a2
50 a2
51 $ fileset 'a_b'
51 $ fileset 'a_b'
52 $ fileset '"\xy"'
53 hg: parse error: invalid \x escape
54 [255]
52
55
53 Test files status
56 Test files status
54
57
55 $ rm a1
58 $ rm a1
56 $ hg rm a2
59 $ hg rm a2
57 $ echo b >> b2
60 $ echo b >> b2
58 $ hg cp b1 c1
61 $ hg cp b1 c1
59 $ echo c > c2
62 $ echo c > c2
60 $ echo c > c3
63 $ echo c > c3
61 $ cat > .hgignore <<EOF
64 $ cat > .hgignore <<EOF
62 > \.hgignore
65 > \.hgignore
63 > 2$
66 > 2$
64 > EOF
67 > EOF
65 $ fileset 'modified()'
68 $ fileset 'modified()'
66 b2
69 b2
67 $ fileset 'added()'
70 $ fileset 'added()'
68 c1
71 c1
69 $ fileset 'removed()'
72 $ fileset 'removed()'
70 a2
73 a2
71 $ fileset 'deleted()'
74 $ fileset 'deleted()'
72 a1
75 a1
73 $ fileset 'unknown()'
76 $ fileset 'unknown()'
74 c3
77 c3
75 $ fileset 'ignored()'
78 $ fileset 'ignored()'
76 .hgignore
79 .hgignore
77 c2
80 c2
78 $ fileset 'hgignore()'
81 $ fileset 'hgignore()'
79 a2
82 a2
80 b2
83 b2
81 $ fileset 'clean()'
84 $ fileset 'clean()'
82 b1
85 b1
83 $ fileset 'copied()'
86 $ fileset 'copied()'
84 c1
87 c1
85
88
86 Test files properties
89 Test files properties
87
90
88 >>> file('bin', 'wb').write('\0a')
91 >>> file('bin', 'wb').write('\0a')
89 $ fileset 'binary()'
92 $ fileset 'binary()'
90 $ fileset 'binary() and unknown()'
93 $ fileset 'binary() and unknown()'
91 bin
94 bin
92 $ echo '^bin$' >> .hgignore
95 $ echo '^bin$' >> .hgignore
93 $ fileset 'binary() and ignored()'
96 $ fileset 'binary() and ignored()'
94 bin
97 bin
95 $ hg add bin
98 $ hg add bin
96 $ fileset 'binary()'
99 $ fileset 'binary()'
97 bin
100 bin
98
101
99 $ fileset 'grep("b{1}")'
102 $ fileset 'grep("b{1}")'
100 b2
103 b2
101 c1
104 c1
102 b1
105 b1
103 $ fileset 'grep("missingparens(")'
106 $ fileset 'grep("missingparens(")'
104 hg: parse error: invalid match pattern: unbalanced parenthesis
107 hg: parse error: invalid match pattern: unbalanced parenthesis
105 [255]
108 [255]
106
109
107 #if execbit
110 #if execbit
108 $ chmod +x b2
111 $ chmod +x b2
109 $ fileset 'exec()'
112 $ fileset 'exec()'
110 b2
113 b2
111 #endif
114 #endif
112
115
113 #if symlink
116 #if symlink
114 $ ln -s b2 b2link
117 $ ln -s b2 b2link
115 $ fileset 'symlink() and unknown()'
118 $ fileset 'symlink() and unknown()'
116 b2link
119 b2link
117 $ hg add b2link
120 $ hg add b2link
118 #endif
121 #endif
119
122
120 #if no-windows
123 #if no-windows
121 $ echo foo > con.xml
124 $ echo foo > con.xml
122 $ fileset 'not portable()'
125 $ fileset 'not portable()'
123 con.xml
126 con.xml
124 $ hg --config ui.portablefilenames=ignore add con.xml
127 $ hg --config ui.portablefilenames=ignore add con.xml
125 #endif
128 #endif
126
129
127 >>> file('1k', 'wb').write(' '*1024)
130 >>> file('1k', 'wb').write(' '*1024)
128 >>> file('2k', 'wb').write(' '*2048)
131 >>> file('2k', 'wb').write(' '*2048)
129 $ hg add 1k 2k
132 $ hg add 1k 2k
130 $ fileset 'size("bar")'
133 $ fileset 'size("bar")'
131 hg: parse error: couldn't parse size: bar
134 hg: parse error: couldn't parse size: bar
132 [255]
135 [255]
133 $ fileset 'size(1k)'
136 $ fileset 'size(1k)'
134 1k
137 1k
135 $ fileset '(1k or 2k) and size("< 2k")'
138 $ fileset '(1k or 2k) and size("< 2k")'
136 1k
139 1k
137 $ fileset '(1k or 2k) and size("<=2k")'
140 $ fileset '(1k or 2k) and size("<=2k")'
138 1k
141 1k
139 2k
142 2k
140 $ fileset '(1k or 2k) and size("> 1k")'
143 $ fileset '(1k or 2k) and size("> 1k")'
141 2k
144 2k
142 $ fileset '(1k or 2k) and size(">=1K")'
145 $ fileset '(1k or 2k) and size(">=1K")'
143 1k
146 1k
144 2k
147 2k
145 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
148 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
146 1k
149 1k
147 $ fileset 'size("1M")'
150 $ fileset 'size("1M")'
148 $ fileset 'size("1 GB")'
151 $ fileset 'size("1 GB")'
149
152
150 Test merge states
153 Test merge states
151
154
152 $ hg ci -m manychanges
155 $ hg ci -m manychanges
153 $ hg up -C 0
156 $ hg up -C 0
154 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
157 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
155 $ echo c >> b2
158 $ echo c >> b2
156 $ hg ci -m diverging b2
159 $ hg ci -m diverging b2
157 created new head
160 created new head
158 $ fileset 'resolved()'
161 $ fileset 'resolved()'
159 $ fileset 'unresolved()'
162 $ fileset 'unresolved()'
160 $ hg merge
163 $ hg merge
161 merging b2
164 merging b2
162 warning: conflicts during merge.
165 warning: conflicts during merge.
163 merging b2 incomplete! (edit conflicts, then use 'hg resolve --mark')
166 merging b2 incomplete! (edit conflicts, then use 'hg resolve --mark')
164 * files updated, 0 files merged, * files removed, 1 files unresolved (glob)
167 * files updated, 0 files merged, * files removed, 1 files unresolved (glob)
165 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
168 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
166 [1]
169 [1]
167 $ fileset 'resolved()'
170 $ fileset 'resolved()'
168 $ fileset 'unresolved()'
171 $ fileset 'unresolved()'
169 b2
172 b2
170 $ echo e > b2
173 $ echo e > b2
171 $ hg resolve -m b2
174 $ hg resolve -m b2
172 (no more unresolved files)
175 (no more unresolved files)
173 $ fileset 'resolved()'
176 $ fileset 'resolved()'
174 b2
177 b2
175 $ fileset 'unresolved()'
178 $ fileset 'unresolved()'
176 $ hg ci -m merge
179 $ hg ci -m merge
177
180
178 Test subrepo predicate
181 Test subrepo predicate
179
182
180 $ hg init sub
183 $ hg init sub
181 $ echo a > sub/suba
184 $ echo a > sub/suba
182 $ hg -R sub add sub/suba
185 $ hg -R sub add sub/suba
183 $ hg -R sub ci -m sub
186 $ hg -R sub ci -m sub
184 $ echo 'sub = sub' > .hgsub
187 $ echo 'sub = sub' > .hgsub
185 $ hg init sub2
188 $ hg init sub2
186 $ echo b > sub2/b
189 $ echo b > sub2/b
187 $ hg -R sub2 ci -Am sub2
190 $ hg -R sub2 ci -Am sub2
188 adding b
191 adding b
189 $ echo 'sub2 = sub2' >> .hgsub
192 $ echo 'sub2 = sub2' >> .hgsub
190 $ fileset 'subrepo()'
193 $ fileset 'subrepo()'
191 $ hg add .hgsub
194 $ hg add .hgsub
192 $ fileset 'subrepo()'
195 $ fileset 'subrepo()'
193 sub
196 sub
194 sub2
197 sub2
195 $ fileset 'subrepo("sub")'
198 $ fileset 'subrepo("sub")'
196 sub
199 sub
197 $ fileset 'subrepo("glob:*")'
200 $ fileset 'subrepo("glob:*")'
198 sub
201 sub
199 sub2
202 sub2
200 $ hg ci -m subrepo
203 $ hg ci -m subrepo
201
204
202 Test that .hgsubstate is updated as appropriate during a conversion. The
205 Test that .hgsubstate is updated as appropriate during a conversion. The
203 saverev property is enough to alter the hashes of the subrepo.
206 saverev property is enough to alter the hashes of the subrepo.
204
207
205 $ hg init ../converted
208 $ hg init ../converted
206 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
209 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
207 > sub ../converted/sub
210 > sub ../converted/sub
208 initializing destination ../converted/sub repository
211 initializing destination ../converted/sub repository
209 scanning source...
212 scanning source...
210 sorting...
213 sorting...
211 converting...
214 converting...
212 0 sub
215 0 sub
213 $ hg clone -U sub2 ../converted/sub2
216 $ hg clone -U sub2 ../converted/sub2
214 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
217 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
215 > . ../converted
218 > . ../converted
216 scanning source...
219 scanning source...
217 sorting...
220 sorting...
218 converting...
221 converting...
219 4 addfiles
222 4 addfiles
220 3 manychanges
223 3 manychanges
221 2 diverging
224 2 diverging
222 1 merge
225 1 merge
223 0 subrepo
226 0 subrepo
224 no ".hgsubstate" updates will be made for "sub2"
227 no ".hgsubstate" updates will be made for "sub2"
225 $ hg up -q -R ../converted -r tip
228 $ hg up -q -R ../converted -r tip
226 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
229 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
227 a
230 a
228 b
231 b
229 $ oldnode=`hg log -r tip -T "{node}\n"`
232 $ oldnode=`hg log -r tip -T "{node}\n"`
230 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
233 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
231 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
234 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
232
235
233 Test with a revision
236 Test with a revision
234
237
235 $ hg log -G --template '{rev} {desc}\n'
238 $ hg log -G --template '{rev} {desc}\n'
236 @ 4 subrepo
239 @ 4 subrepo
237 |
240 |
238 o 3 merge
241 o 3 merge
239 |\
242 |\
240 | o 2 diverging
243 | o 2 diverging
241 | |
244 | |
242 o | 1 manychanges
245 o | 1 manychanges
243 |/
246 |/
244 o 0 addfiles
247 o 0 addfiles
245
248
246 $ echo unknown > unknown
249 $ echo unknown > unknown
247 $ fileset -r1 'modified()'
250 $ fileset -r1 'modified()'
248 b2
251 b2
249 $ fileset -r1 'added() and c1'
252 $ fileset -r1 'added() and c1'
250 c1
253 c1
251 $ fileset -r1 'removed()'
254 $ fileset -r1 'removed()'
252 a2
255 a2
253 $ fileset -r1 'deleted()'
256 $ fileset -r1 'deleted()'
254 $ fileset -r1 'unknown()'
257 $ fileset -r1 'unknown()'
255 $ fileset -r1 'ignored()'
258 $ fileset -r1 'ignored()'
256 $ fileset -r1 'hgignore()'
259 $ fileset -r1 'hgignore()'
257 b2
260 b2
258 bin
261 bin
259 $ fileset -r1 'binary()'
262 $ fileset -r1 'binary()'
260 bin
263 bin
261 $ fileset -r1 'size(1k)'
264 $ fileset -r1 'size(1k)'
262 1k
265 1k
263 $ fileset -r3 'resolved()'
266 $ fileset -r3 'resolved()'
264 $ fileset -r3 'unresolved()'
267 $ fileset -r3 'unresolved()'
265
268
266 #if execbit
269 #if execbit
267 $ fileset -r1 'exec()'
270 $ fileset -r1 'exec()'
268 b2
271 b2
269 #endif
272 #endif
270
273
271 #if symlink
274 #if symlink
272 $ fileset -r1 'symlink()'
275 $ fileset -r1 'symlink()'
273 b2link
276 b2link
274 #endif
277 #endif
275
278
276 #if no-windows
279 #if no-windows
277 $ fileset -r1 'not portable()'
280 $ fileset -r1 'not portable()'
278 con.xml
281 con.xml
279 $ hg forget 'con.xml'
282 $ hg forget 'con.xml'
280 #endif
283 #endif
281
284
282 $ fileset -r4 'subrepo("re:su.*")'
285 $ fileset -r4 'subrepo("re:su.*")'
283 sub
286 sub
284 sub2
287 sub2
285 $ fileset -r4 'subrepo("sub")'
288 $ fileset -r4 'subrepo("sub")'
286 sub
289 sub
287 $ fileset -r4 'b2 or c1'
290 $ fileset -r4 'b2 or c1'
288 b2
291 b2
289 c1
292 c1
290
293
291 >>> open('dos', 'wb').write("dos\r\n")
294 >>> open('dos', 'wb').write("dos\r\n")
292 >>> open('mixed', 'wb').write("dos\r\nunix\n")
295 >>> open('mixed', 'wb').write("dos\r\nunix\n")
293 >>> open('mac', 'wb').write("mac\r")
296 >>> open('mac', 'wb').write("mac\r")
294 $ hg add dos mixed mac
297 $ hg add dos mixed mac
295
298
296 $ fileset 'eol(dos)'
299 $ fileset 'eol(dos)'
297 dos
300 dos
298 mixed
301 mixed
299 $ fileset 'eol(unix)'
302 $ fileset 'eol(unix)'
300 .hgsub
303 .hgsub
301 .hgsubstate
304 .hgsubstate
302 a1
305 a1
303 b1
306 b1
304 b2
307 b2
305 c1
308 c1
306 mixed
309 mixed
307 $ fileset 'eol(mac)'
310 $ fileset 'eol(mac)'
308 mac
311 mac
General Comments 0
You need to be logged in to leave comments. Login now