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