##// END OF EJS Templates
fileset: optimize 'x and not y' to 'x - y'...
Yuya Nishihara -
r38868:ca4de8ba default
parent child Browse files
Show More
@@ -1,220 +1,223
1 1 # filesetlang.py - parser, tokenizer and utility for file set language
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 from .i18n import _
11 11 from . import (
12 12 error,
13 13 parser,
14 14 pycompat,
15 15 )
16 16
17 17 elements = {
18 18 # token-type: binding-strength, primary, prefix, infix, suffix
19 19 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
20 20 ":": (15, None, None, ("kindpat", 15), None),
21 21 "-": (5, None, ("negate", 19), ("minus", 5), None),
22 22 "not": (10, None, ("not", 10), None, None),
23 23 "!": (10, None, ("not", 10), None, None),
24 24 "and": (5, None, None, ("and", 5), None),
25 25 "&": (5, None, None, ("and", 5), None),
26 26 "or": (4, None, None, ("or", 4), None),
27 27 "|": (4, None, None, ("or", 4), None),
28 28 "+": (4, None, None, ("or", 4), None),
29 29 ",": (2, None, None, ("list", 2), None),
30 30 ")": (0, None, None, None, None),
31 31 "symbol": (0, "symbol", None, None, None),
32 32 "string": (0, "string", None, None, None),
33 33 "end": (0, None, None, None, None),
34 34 }
35 35
36 36 keywords = {'and', 'or', 'not'}
37 37
38 38 symbols = {}
39 39
40 40 globchars = ".*{}[]?/\\_"
41 41
42 42 def tokenize(program):
43 43 pos, l = 0, len(program)
44 44 program = pycompat.bytestr(program)
45 45 while pos < l:
46 46 c = program[pos]
47 47 if c.isspace(): # skip inter-token whitespace
48 48 pass
49 49 elif c in "(),-:|&+!": # handle simple operators
50 50 yield (c, None, pos)
51 51 elif (c in '"\'' or c == 'r' and
52 52 program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
53 53 if c == 'r':
54 54 pos += 1
55 55 c = program[pos]
56 56 decode = lambda x: x
57 57 else:
58 58 decode = parser.unescapestr
59 59 pos += 1
60 60 s = pos
61 61 while pos < l: # find closing quote
62 62 d = program[pos]
63 63 if d == '\\': # skip over escaped characters
64 64 pos += 2
65 65 continue
66 66 if d == c:
67 67 yield ('string', decode(program[s:pos]), s)
68 68 break
69 69 pos += 1
70 70 else:
71 71 raise error.ParseError(_("unterminated string"), s)
72 72 elif c.isalnum() or c in globchars or ord(c) > 127:
73 73 # gather up a symbol/keyword
74 74 s = pos
75 75 pos += 1
76 76 while pos < l: # find end of symbol
77 77 d = program[pos]
78 78 if not (d.isalnum() or d in globchars or ord(d) > 127):
79 79 break
80 80 pos += 1
81 81 sym = program[s:pos]
82 82 if sym in keywords: # operator keywords
83 83 yield (sym, None, s)
84 84 else:
85 85 yield ('symbol', sym, s)
86 86 pos -= 1
87 87 else:
88 88 raise error.ParseError(_("syntax error"), pos)
89 89 pos += 1
90 90 yield ('end', None, pos)
91 91
92 92 def parse(expr):
93 93 p = parser.parser(elements)
94 94 tree, pos = p.parse(tokenize(expr))
95 95 if pos != len(expr):
96 96 raise error.ParseError(_("invalid token"), pos)
97 97 return parser.simplifyinfixops(tree, {'list', 'or'})
98 98
99 99 def getsymbol(x):
100 100 if x and x[0] == 'symbol':
101 101 return x[1]
102 102 raise error.ParseError(_('not a symbol'))
103 103
104 104 def getstring(x, err):
105 105 if x and (x[0] == 'string' or x[0] == 'symbol'):
106 106 return x[1]
107 107 raise error.ParseError(err)
108 108
109 109 def getkindpat(x, y, allkinds, err):
110 110 kind = getsymbol(x)
111 111 pat = getstring(y, err)
112 112 if kind not in allkinds:
113 113 raise error.ParseError(_("invalid pattern kind: %s") % kind)
114 114 return '%s:%s' % (kind, pat)
115 115
116 116 def getpattern(x, allkinds, err):
117 117 if x and x[0] == 'kindpat':
118 118 return getkindpat(x[1], x[2], allkinds, err)
119 119 return getstring(x, err)
120 120
121 121 def getlist(x):
122 122 if not x:
123 123 return []
124 124 if x[0] == 'list':
125 125 return list(x[1:])
126 126 return [x]
127 127
128 128 def getargs(x, min, max, err):
129 129 l = getlist(x)
130 130 if len(l) < min or len(l) > max:
131 131 raise error.ParseError(err)
132 132 return l
133 133
134 134 def _analyze(x):
135 135 if x is None:
136 136 return x
137 137
138 138 op = x[0]
139 139 if op in {'string', 'symbol'}:
140 140 return x
141 141 if op == 'kindpat':
142 142 getsymbol(x[1]) # kind must be a symbol
143 143 t = _analyze(x[2])
144 144 return (op, x[1], t)
145 145 if op == 'group':
146 146 return _analyze(x[1])
147 147 if op == 'negate':
148 148 raise error.ParseError(_("can't use negate operator in this context"))
149 149 if op == 'not':
150 150 t = _analyze(x[1])
151 151 return (op, t)
152 if op in {'and', 'minus'}:
152 if op == 'and':
153 153 ta = _analyze(x[1])
154 154 tb = _analyze(x[2])
155 155 return (op, ta, tb)
156 if op == 'minus':
157 return _analyze(('and', x[1], ('not', x[2])))
156 158 if op in {'list', 'or'}:
157 159 ts = tuple(_analyze(y) for y in x[1:])
158 160 return (op,) + ts
159 161 if op == 'func':
160 162 getsymbol(x[1]) # function name must be a symbol
161 163 ta = _analyze(x[2])
162 164 return (op, x[1], ta)
163 165 raise error.ProgrammingError('invalid operator %r' % op)
164 166
165 167 def analyze(x):
166 168 """Transform raw parsed tree to evaluatable tree which can be fed to
167 169 optimize() or getmatch()
168 170
169 171 All pseudo operations should be mapped to real operations or functions
170 172 defined in methods or symbols table respectively.
171 173 """
172 174 return _analyze(x)
173 175
176 def _optimizeandops(op, ta, tb):
177 if tb is not None and tb[0] == 'not':
178 return ('minus', ta, tb[1])
179 return (op, ta, tb)
180
174 181 def _optimize(x):
175 182 if x is None:
176 183 return 0, x
177 184
178 185 op = x[0]
179 186 if op in {'string', 'symbol'}:
180 187 return 0.5, x
181 188 if op == 'kindpat':
182 189 w, t = _optimize(x[2])
183 190 return w, (op, x[1], t)
184 191 if op == 'not':
185 192 w, t = _optimize(x[1])
186 193 return w, (op, t)
187 194 if op == 'and':
188 195 wa, ta = _optimize(x[1])
189 196 wb, tb = _optimize(x[2])
190 197 if wa <= wb:
191 return wa, (op, ta, tb)
198 return wa, _optimizeandops(op, ta, tb)
192 199 else:
193 return wb, (op, tb, ta)
194 if op == 'minus':
195 wa, ta = _optimize(x[1])
196 wb, tb = _optimize(x[2])
197 return max(wa, wb), (op, ta, tb)
200 return wb, _optimizeandops(op, tb, ta)
198 201 if op == 'or':
199 202 ws, ts = zip(*(_optimize(y) for y in x[1:]))
200 203 return max(ws), (op,) + ts
201 204 if op == 'list':
202 205 ws, ts = zip(*(_optimize(y) for y in x[1:]))
203 206 return sum(ws), (op,) + ts
204 207 if op == 'func':
205 208 f = getsymbol(x[1])
206 209 w = getattr(symbols.get(f), '_weight', 1)
207 210 wa, ta = _optimize(x[2])
208 211 return w + wa, (op, x[1], ta)
209 212 raise error.ProgrammingError('invalid operator %r' % op)
210 213
211 214 def optimize(x):
212 215 """Reorder/rewrite evaluatable tree for optimization
213 216
214 217 All pseudo operations should be transformed beforehand.
215 218 """
216 219 _w, t = _optimize(x)
217 220 return t
218 221
219 222 def prettyformat(tree):
220 223 return parser.prettyformat(tree, ('string', 'symbol'))
@@ -1,778 +1,845
1 1 $ fileset() {
2 2 > hg debugfileset --all-files "$@"
3 3 > }
4 4
5 5 $ hg init repo
6 6 $ cd repo
7 7 $ echo a > a1
8 8 $ echo a > a2
9 9 $ echo b > b1
10 10 $ echo b > b2
11 11 $ hg ci -Am addfiles
12 12 adding a1
13 13 adding a2
14 14 adding b1
15 15 adding b2
16 16
17 17 Test operators and basic patterns
18 18
19 19 $ fileset -v a1
20 20 (symbol 'a1')
21 21 * matcher:
22 22 <patternmatcher patterns='(?:a1$)'>
23 23 a1
24 24 $ fileset -v 'a*'
25 25 (symbol 'a*')
26 26 * matcher:
27 27 <patternmatcher patterns='(?:a[^/]*$)'>
28 28 a1
29 29 a2
30 30 $ fileset -v '"re:a\d"'
31 31 (string 're:a\\d')
32 32 * matcher:
33 33 <patternmatcher patterns='(?:a\\d)'>
34 34 a1
35 35 a2
36 36 $ fileset -v '!re:"a\d"'
37 37 (not
38 38 (kindpat
39 39 (symbol 're')
40 40 (string 'a\\d')))
41 41 * matcher:
42 42 <predicatenmatcher
43 43 pred=<not
44 44 <patternmatcher patterns='(?:a\\d)'>>>
45 45 b1
46 46 b2
47 47 $ fileset -v 'path:a1 or glob:b?'
48 48 (or
49 49 (kindpat
50 50 (symbol 'path')
51 51 (symbol 'a1'))
52 52 (kindpat
53 53 (symbol 'glob')
54 54 (symbol 'b?')))
55 55 * matcher:
56 56 <unionmatcher matchers=[
57 57 <patternmatcher patterns='(?:a1(?:/|$))'>,
58 58 <patternmatcher patterns='(?:b.$)'>]>
59 59 a1
60 60 b1
61 61 b2
62 62 $ fileset -v --no-show-matcher 'a1 or a2'
63 63 (or
64 64 (symbol 'a1')
65 65 (symbol 'a2'))
66 66 a1
67 67 a2
68 68 $ fileset 'a1 | a2'
69 69 a1
70 70 a2
71 71 $ fileset 'a* and "*1"'
72 72 a1
73 73 $ fileset 'a* & "*1"'
74 74 a1
75 75 $ fileset 'not (r"a*")'
76 76 b1
77 77 b2
78 78 $ fileset '! ("a*")'
79 79 b1
80 80 b2
81 81 $ fileset 'a* - a1'
82 82 a2
83 83 $ fileset 'a_b'
84 84 $ fileset '"\xy"'
85 85 hg: parse error: invalid \x escape* (glob)
86 86 [255]
87 87
88 88 Test invalid syntax
89 89
90 90 $ fileset -v '"added"()'
91 91 (func
92 92 (string 'added')
93 93 None)
94 94 hg: parse error: not a symbol
95 95 [255]
96 96 $ fileset -v '()()'
97 97 (func
98 98 (group
99 99 None)
100 100 None)
101 101 hg: parse error: not a symbol
102 102 [255]
103 103 $ fileset -v -- '-x'
104 104 (negate
105 105 (symbol 'x'))
106 106 hg: parse error: can't use negate operator in this context
107 107 [255]
108 108 $ fileset -v -- '-()'
109 109 (negate
110 110 (group
111 111 None))
112 112 hg: parse error: can't use negate operator in this context
113 113 [255]
114 114 $ fileset -p parsed 'a, b, c'
115 115 * parsed:
116 116 (list
117 117 (symbol 'a')
118 118 (symbol 'b')
119 119 (symbol 'c'))
120 120 hg: parse error: can't use a list in this context
121 121 (see 'hg help "filesets.x or y"')
122 122 [255]
123 123
124 124 $ fileset '"path":.'
125 125 hg: parse error: not a symbol
126 126 [255]
127 127 $ fileset 'path:foo bar'
128 128 hg: parse error at 9: invalid token
129 129 [255]
130 130 $ fileset 'foo:bar:baz'
131 131 hg: parse error: not a symbol
132 132 [255]
133 133 $ fileset 'foo:bar()'
134 134 hg: parse error: pattern must be a string
135 135 [255]
136 136 $ fileset 'foo:bar'
137 137 hg: parse error: invalid pattern kind: foo
138 138 [255]
139 139
140 140 Show parsed tree at stages:
141 141
142 142 $ fileset -p unknown a
143 143 abort: invalid stage name: unknown
144 144 [255]
145 145
146 146 $ fileset -p parsed 'path:a1 or glob:b?'
147 147 * parsed:
148 148 (or
149 149 (kindpat
150 150 (symbol 'path')
151 151 (symbol 'a1'))
152 152 (kindpat
153 153 (symbol 'glob')
154 154 (symbol 'b?')))
155 155 a1
156 156 b1
157 157 b2
158 158
159 159 $ fileset -p all -s 'a1 or a2 or (grep("b") & clean())'
160 160 * parsed:
161 161 (or
162 162 (symbol 'a1')
163 163 (symbol 'a2')
164 164 (group
165 165 (and
166 166 (func
167 167 (symbol 'grep')
168 168 (string 'b'))
169 169 (func
170 170 (symbol 'clean')
171 171 None))))
172 172 * analyzed:
173 173 (or
174 174 (symbol 'a1')
175 175 (symbol 'a2')
176 176 (and
177 177 (func
178 178 (symbol 'grep')
179 179 (string 'b'))
180 180 (func
181 181 (symbol 'clean')
182 182 None)))
183 183 * optimized:
184 184 (or
185 185 (symbol 'a1')
186 186 (symbol 'a2')
187 187 (and
188 188 (func
189 189 (symbol 'clean')
190 190 None)
191 191 (func
192 192 (symbol 'grep')
193 193 (string 'b'))))
194 194 * matcher:
195 195 <unionmatcher matchers=[
196 196 <patternmatcher patterns='(?:a1$)'>,
197 197 <patternmatcher patterns='(?:a2$)'>,
198 198 <intersectionmatcher
199 199 m1=<predicatenmatcher pred=clean>,
200 200 m2=<predicatenmatcher pred=grep('b')>>]>
201 201 a1
202 202 a2
203 203 b1
204 204 b2
205 205
206 Use differencematcher for 'x and not y':
207
208 $ fileset -p optimized -s 'a* and not a1'
209 * optimized:
210 (minus
211 (symbol 'a*')
212 (symbol 'a1'))
213 * matcher:
214 <differencematcher
215 m1=<patternmatcher patterns='(?:a[^/]*$)'>,
216 m2=<patternmatcher patterns='(?:a1$)'>>
217 a2
218
219 $ fileset -p optimized -s '!binary() and a*'
220 * optimized:
221 (minus
222 (symbol 'a*')
223 (func
224 (symbol 'binary')
225 None))
226 * matcher:
227 <differencematcher
228 m1=<patternmatcher patterns='(?:a[^/]*$)'>,
229 m2=<predicatenmatcher pred=binary>>
230 a1
231 a2
232
233 'x - y' is rewritten to 'x and not y' first so the operands can be reordered:
234
235 $ fileset -p analyzed -p optimized -s 'a* - a1'
236 * analyzed:
237 (and
238 (symbol 'a*')
239 (not
240 (symbol 'a1')))
241 * optimized:
242 (minus
243 (symbol 'a*')
244 (symbol 'a1'))
245 * matcher:
246 <differencematcher
247 m1=<patternmatcher patterns='(?:a[^/]*$)'>,
248 m2=<patternmatcher patterns='(?:a1$)'>>
249 a2
250
251 $ fileset -p analyzed -p optimized -s 'binary() - a*'
252 * analyzed:
253 (and
254 (func
255 (symbol 'binary')
256 None)
257 (not
258 (symbol 'a*')))
259 * optimized:
260 (and
261 (not
262 (symbol 'a*'))
263 (func
264 (symbol 'binary')
265 None))
266 * matcher:
267 <intersectionmatcher
268 m1=<predicatenmatcher
269 pred=<not
270 <patternmatcher patterns='(?:a[^/]*$)'>>>,
271 m2=<predicatenmatcher pred=binary>>
272
206 273 Test files status
207 274
208 275 $ rm a1
209 276 $ hg rm a2
210 277 $ echo b >> b2
211 278 $ hg cp b1 c1
212 279 $ echo c > c2
213 280 $ echo c > c3
214 281 $ cat > .hgignore <<EOF
215 282 > \.hgignore
216 283 > 2$
217 284 > EOF
218 285 $ fileset 'modified()'
219 286 b2
220 287 $ fileset 'added()'
221 288 c1
222 289 $ fileset 'removed()'
223 290 a2
224 291 $ fileset 'deleted()'
225 292 a1
226 293 $ fileset 'missing()'
227 294 a1
228 295 $ fileset 'unknown()'
229 296 c3
230 297 $ fileset 'ignored()'
231 298 .hgignore
232 299 c2
233 300 $ fileset 'hgignore()'
234 301 .hgignore
235 302 a2
236 303 b2
237 304 c2
238 305 $ fileset 'clean()'
239 306 b1
240 307 $ fileset 'copied()'
241 308 c1
242 309
243 310 Test files status in different revisions
244 311
245 312 $ hg status -m
246 313 M b2
247 314 $ fileset -r0 'revs("wdir()", modified())' --traceback
248 315 b2
249 316 $ hg status -a
250 317 A c1
251 318 $ fileset -r0 'revs("wdir()", added())'
252 319 c1
253 320 $ hg status --change 0 -a
254 321 A a1
255 322 A a2
256 323 A b1
257 324 A b2
258 325 $ hg status -mru
259 326 M b2
260 327 R a2
261 328 ? c3
262 329 $ fileset -r0 'added() and revs("wdir()", modified() or removed() or unknown())'
263 330 a2
264 331 b2
265 332 $ fileset -r0 'added() or revs("wdir()", added())'
266 333 a1
267 334 a2
268 335 b1
269 336 b2
270 337 c1
271 338
272 339 Test files properties
273 340
274 341 >>> open('bin', 'wb').write(b'\0a') and None
275 342 $ fileset 'binary()'
276 343 bin
277 344 $ fileset 'binary() and unknown()'
278 345 bin
279 346 $ echo '^bin$' >> .hgignore
280 347 $ fileset 'binary() and ignored()'
281 348 bin
282 349 $ hg add bin
283 350 $ fileset 'binary()'
284 351 bin
285 352
286 353 $ fileset -p optimized -s 'binary() and b*'
287 354 * optimized:
288 355 (and
289 356 (symbol 'b*')
290 357 (func
291 358 (symbol 'binary')
292 359 None))
293 360 * matcher:
294 361 <intersectionmatcher
295 362 m1=<patternmatcher patterns='(?:b[^/]*$)'>,
296 363 m2=<predicatenmatcher pred=binary>>
297 364 bin
298 365
299 366 $ fileset 'grep("b{1}")'
300 367 .hgignore
301 368 b1
302 369 b2
303 370 c1
304 371 $ fileset 'grep("missingparens(")'
305 372 hg: parse error: invalid match pattern: (unbalanced parenthesis|missing \)).* (re)
306 373 [255]
307 374
308 375 #if execbit
309 376 $ chmod +x b2
310 377 $ fileset 'exec()'
311 378 b2
312 379 #endif
313 380
314 381 #if symlink
315 382 $ ln -s b2 b2link
316 383 $ fileset 'symlink() and unknown()'
317 384 b2link
318 385 $ hg add b2link
319 386 #endif
320 387
321 388 #if no-windows
322 389 $ echo foo > con.xml
323 390 $ fileset 'not portable()'
324 391 con.xml
325 392 $ hg --config ui.portablefilenames=ignore add con.xml
326 393 #endif
327 394
328 395 >>> open('1k', 'wb').write(b' '*1024) and None
329 396 >>> open('2k', 'wb').write(b' '*2048) and None
330 397 $ hg add 1k 2k
331 398 $ fileset 'size("bar")'
332 399 hg: parse error: couldn't parse size: bar
333 400 [255]
334 401 $ fileset '(1k, 2k)'
335 402 hg: parse error: can't use a list in this context
336 403 (see 'hg help "filesets.x or y"')
337 404 [255]
338 405 $ fileset 'size(1k)'
339 406 1k
340 407 $ fileset '(1k or 2k) and size("< 2k")'
341 408 1k
342 409 $ fileset '(1k or 2k) and size("<=2k")'
343 410 1k
344 411 2k
345 412 $ fileset '(1k or 2k) and size("> 1k")'
346 413 2k
347 414 $ fileset '(1k or 2k) and size(">=1K")'
348 415 1k
349 416 2k
350 417 $ fileset '(1k or 2k) and size(".5KB - 1.5kB")'
351 418 1k
352 419 $ fileset 'size("1M")'
353 420 $ fileset 'size("1 GB")'
354 421
355 422 Test merge states
356 423
357 424 $ hg ci -m manychanges
358 425 $ hg file -r . 'set:copied() & modified()'
359 426 [1]
360 427 $ hg up -C 0
361 428 * files updated, 0 files merged, * files removed, 0 files unresolved (glob)
362 429 $ echo c >> b2
363 430 $ hg ci -m diverging b2
364 431 created new head
365 432 $ fileset 'resolved()'
366 433 $ fileset 'unresolved()'
367 434 $ hg merge
368 435 merging b2
369 436 warning: conflicts while merging b2! (edit, then use 'hg resolve --mark')
370 437 * files updated, 0 files merged, 1 files removed, 1 files unresolved (glob)
371 438 use 'hg resolve' to retry unresolved file merges or 'hg merge --abort' to abandon
372 439 [1]
373 440 $ fileset 'resolved()'
374 441 $ fileset 'unresolved()'
375 442 b2
376 443 $ echo e > b2
377 444 $ hg resolve -m b2
378 445 (no more unresolved files)
379 446 $ fileset 'resolved()'
380 447 b2
381 448 $ fileset 'unresolved()'
382 449 $ hg ci -m merge
383 450
384 451 Test subrepo predicate
385 452
386 453 $ hg init sub
387 454 $ echo a > sub/suba
388 455 $ hg -R sub add sub/suba
389 456 $ hg -R sub ci -m sub
390 457 $ echo 'sub = sub' > .hgsub
391 458 $ hg init sub2
392 459 $ echo b > sub2/b
393 460 $ hg -R sub2 ci -Am sub2
394 461 adding b
395 462 $ echo 'sub2 = sub2' >> .hgsub
396 463 $ fileset 'subrepo()'
397 464 $ hg add .hgsub
398 465 $ fileset 'subrepo()'
399 466 sub
400 467 sub2
401 468 $ fileset 'subrepo("sub")'
402 469 sub
403 470 $ fileset 'subrepo("glob:*")'
404 471 sub
405 472 sub2
406 473 $ hg ci -m subrepo
407 474
408 475 Test that .hgsubstate is updated as appropriate during a conversion. The
409 476 saverev property is enough to alter the hashes of the subrepo.
410 477
411 478 $ hg init ../converted
412 479 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
413 480 > sub ../converted/sub
414 481 initializing destination ../converted/sub repository
415 482 scanning source...
416 483 sorting...
417 484 converting...
418 485 0 sub
419 486 $ hg clone -U sub2 ../converted/sub2
420 487 $ hg --config extensions.convert= convert --config convert.hg.saverev=True \
421 488 > . ../converted
422 489 scanning source...
423 490 sorting...
424 491 converting...
425 492 4 addfiles
426 493 3 manychanges
427 494 2 diverging
428 495 1 merge
429 496 0 subrepo
430 497 no ".hgsubstate" updates will be made for "sub2"
431 498 $ hg up -q -R ../converted -r tip
432 499 $ hg --cwd ../converted cat sub/suba sub2/b -r tip
433 500 a
434 501 b
435 502 $ oldnode=`hg log -r tip -T "{node}\n"`
436 503 $ newnode=`hg log -R ../converted -r tip -T "{node}\n"`
437 504 $ [ "$oldnode" != "$newnode" ] || echo "nothing changed"
438 505
439 506 Test with a revision
440 507
441 508 $ hg log -G --template '{rev} {desc}\n'
442 509 @ 4 subrepo
443 510 |
444 511 o 3 merge
445 512 |\
446 513 | o 2 diverging
447 514 | |
448 515 o | 1 manychanges
449 516 |/
450 517 o 0 addfiles
451 518
452 519 $ echo unknown > unknown
453 520 $ fileset -r1 'modified()'
454 521 b2
455 522 $ fileset -r1 'added() and c1'
456 523 c1
457 524 $ fileset -r1 'removed()'
458 525 a2
459 526 $ fileset -r1 'deleted()'
460 527 $ fileset -r1 'unknown()'
461 528 $ fileset -r1 'ignored()'
462 529 $ fileset -r1 'hgignore()'
463 530 .hgignore
464 531 a2
465 532 b2
466 533 bin
467 534 c2
468 535 sub2
469 536 $ fileset -r1 'binary()'
470 537 bin
471 538 $ fileset -r1 'size(1k)'
472 539 1k
473 540 $ fileset -r3 'resolved()'
474 541 $ fileset -r3 'unresolved()'
475 542
476 543 #if execbit
477 544 $ fileset -r1 'exec()'
478 545 b2
479 546 #endif
480 547
481 548 #if symlink
482 549 $ fileset -r1 'symlink()'
483 550 b2link
484 551 #endif
485 552
486 553 #if no-windows
487 554 $ fileset -r1 'not portable()'
488 555 con.xml
489 556 $ hg forget 'con.xml'
490 557 #endif
491 558
492 559 $ fileset -r4 'subrepo("re:su.*")'
493 560 sub
494 561 sub2
495 562 $ fileset -r4 'subrepo(re:su.*)'
496 563 sub
497 564 sub2
498 565 $ fileset -r4 'subrepo("sub")'
499 566 sub
500 567 $ fileset -r4 'b2 or c1'
501 568 b2
502 569 c1
503 570
504 571 >>> open('dos', 'wb').write(b"dos\r\n") and None
505 572 >>> open('mixed', 'wb').write(b"dos\r\nunix\n") and None
506 573 >>> open('mac', 'wb').write(b"mac\r") and None
507 574 $ hg add dos mixed mac
508 575
509 576 (remove a1, to examine safety of 'eol' on removed files)
510 577 $ rm a1
511 578
512 579 $ fileset 'eol(dos)'
513 580 dos
514 581 mixed
515 582 $ fileset 'eol(unix)'
516 583 .hgignore
517 584 .hgsub
518 585 .hgsubstate
519 586 b1
520 587 b2
521 588 b2.orig
522 589 c1
523 590 c2
524 591 c3
525 592 con.xml (no-windows !)
526 593 mixed
527 594 unknown
528 595 $ fileset 'eol(mac)'
529 596 mac
530 597
531 598 Test safety of 'encoding' on removed files
532 599
533 600 $ fileset 'encoding("ascii")'
534 601 .hgignore
535 602 .hgsub
536 603 .hgsubstate
537 604 1k
538 605 2k
539 606 b1
540 607 b2
541 608 b2.orig
542 609 b2link (symlink !)
543 610 bin
544 611 c1
545 612 c2
546 613 c3
547 614 con.xml (no-windows !)
548 615 dos
549 616 mac
550 617 mixed
551 618 unknown
552 619
553 620 Test 'revs(...)'
554 621 ================
555 622
556 623 small reminder of the repository state
557 624
558 625 $ hg log -G
559 626 @ changeset: 4:* (glob)
560 627 | tag: tip
561 628 | user: test
562 629 | date: Thu Jan 01 00:00:00 1970 +0000
563 630 | summary: subrepo
564 631 |
565 632 o changeset: 3:* (glob)
566 633 |\ parent: 2:55b05bdebf36
567 634 | | parent: 1:* (glob)
568 635 | | user: test
569 636 | | date: Thu Jan 01 00:00:00 1970 +0000
570 637 | | summary: merge
571 638 | |
572 639 | o changeset: 2:55b05bdebf36
573 640 | | parent: 0:8a9576c51c1f
574 641 | | user: test
575 642 | | date: Thu Jan 01 00:00:00 1970 +0000
576 643 | | summary: diverging
577 644 | |
578 645 o | changeset: 1:* (glob)
579 646 |/ user: test
580 647 | date: Thu Jan 01 00:00:00 1970 +0000
581 648 | summary: manychanges
582 649 |
583 650 o changeset: 0:8a9576c51c1f
584 651 user: test
585 652 date: Thu Jan 01 00:00:00 1970 +0000
586 653 summary: addfiles
587 654
588 655 $ hg status --change 0
589 656 A a1
590 657 A a2
591 658 A b1
592 659 A b2
593 660 $ hg status --change 1
594 661 M b2
595 662 A 1k
596 663 A 2k
597 664 A b2link (no-windows !)
598 665 A bin
599 666 A c1
600 667 A con.xml (no-windows !)
601 668 R a2
602 669 $ hg status --change 2
603 670 M b2
604 671 $ hg status --change 3
605 672 M b2
606 673 A 1k
607 674 A 2k
608 675 A b2link (no-windows !)
609 676 A bin
610 677 A c1
611 678 A con.xml (no-windows !)
612 679 R a2
613 680 $ hg status --change 4
614 681 A .hgsub
615 682 A .hgsubstate
616 683 $ hg status
617 684 A dos
618 685 A mac
619 686 A mixed
620 687 R con.xml (no-windows !)
621 688 ! a1
622 689 ? b2.orig
623 690 ? c3
624 691 ? unknown
625 692
626 693 Test files at -r0 should be filtered by files at wdir
627 694 -----------------------------------------------------
628 695
629 696 $ fileset -r0 'tracked() and revs("wdir()", tracked())'
630 697 a1
631 698 b1
632 699 b2
633 700
634 701 Test that "revs()" work at all
635 702 ------------------------------
636 703
637 704 $ fileset "revs('2', modified())"
638 705 b2
639 706
640 707 Test that "revs()" work for file missing in the working copy/current context
641 708 ----------------------------------------------------------------------------
642 709
643 710 (a2 not in working copy)
644 711
645 712 $ fileset "revs('0', added())"
646 713 a1
647 714 a2
648 715 b1
649 716 b2
650 717
651 718 (none of the file exist in "0")
652 719
653 720 $ fileset -r 0 "revs('4', added())"
654 721 .hgsub
655 722 .hgsubstate
656 723
657 724 Call with empty revset
658 725 --------------------------
659 726
660 727 $ fileset "revs('2-2', modified())"
661 728
662 729 Call with revset matching multiple revs
663 730 ---------------------------------------
664 731
665 732 $ fileset "revs('0+4', added())"
666 733 .hgsub
667 734 .hgsubstate
668 735 a1
669 736 a2
670 737 b1
671 738 b2
672 739
673 740 overlapping set
674 741
675 742 $ fileset "revs('1+2', modified())"
676 743 b2
677 744
678 745 test 'status(...)'
679 746 =================
680 747
681 748 Simple case
682 749 -----------
683 750
684 751 $ fileset "status(3, 4, added())"
685 752 .hgsub
686 753 .hgsubstate
687 754
688 755 use rev to restrict matched file
689 756 -----------------------------------------
690 757
691 758 $ hg status --removed --rev 0 --rev 1
692 759 R a2
693 760 $ fileset "status(0, 1, removed())"
694 761 a2
695 762 $ fileset "tracked() and status(0, 1, removed())"
696 763 $ fileset -r 4 "status(0, 1, removed())"
697 764 a2
698 765 $ fileset -r 4 "tracked() and status(0, 1, removed())"
699 766 $ fileset "revs('4', tracked() and status(0, 1, removed()))"
700 767 $ fileset "revs('0', tracked() and status(0, 1, removed()))"
701 768 a2
702 769
703 770 check wdir()
704 771 ------------
705 772
706 773 $ hg status --removed --rev 4
707 774 R con.xml (no-windows !)
708 775 $ fileset "status(4, 'wdir()', removed())"
709 776 con.xml (no-windows !)
710 777
711 778 $ hg status --removed --rev 2
712 779 R a2
713 780 $ fileset "status('2', 'wdir()', removed())"
714 781 a2
715 782
716 783 test backward status
717 784 --------------------
718 785
719 786 $ hg status --removed --rev 0 --rev 4
720 787 R a2
721 788 $ hg status --added --rev 4 --rev 0
722 789 A a2
723 790 $ fileset "status(4, 0, added())"
724 791 a2
725 792
726 793 test cross branch status
727 794 ------------------------
728 795
729 796 $ hg status --added --rev 1 --rev 2
730 797 A a2
731 798 $ fileset "status(1, 2, added())"
732 799 a2
733 800
734 801 test with multi revs revset
735 802 ---------------------------
736 803 $ hg status --added --rev 0:1 --rev 3:4
737 804 A .hgsub
738 805 A .hgsubstate
739 806 A 1k
740 807 A 2k
741 808 A b2link (no-windows !)
742 809 A bin
743 810 A c1
744 811 A con.xml (no-windows !)
745 812 $ fileset "status('0:1', '3:4', added())"
746 813 .hgsub
747 814 .hgsubstate
748 815 1k
749 816 2k
750 817 b2link (no-windows !)
751 818 bin
752 819 c1
753 820 con.xml (no-windows !)
754 821
755 822 tests with empty value
756 823 ----------------------
757 824
758 825 Fully empty revset
759 826
760 827 $ fileset "status('', '4', added())"
761 828 hg: parse error: first argument to status must be a revision
762 829 [255]
763 830 $ fileset "status('2', '', added())"
764 831 hg: parse error: second argument to status must be a revision
765 832 [255]
766 833
767 834 Empty revset will error at the revset layer
768 835
769 836 $ fileset "status(' ', '4', added())"
770 837 hg: parse error at 1: not a prefix: end
771 838 (
772 839 ^ here)
773 840 [255]
774 841 $ fileset "status('2', ' ', added())"
775 842 hg: parse error at 1: not a prefix: end
776 843 (
777 844 ^ here)
778 845 [255]
General Comments 0
You need to be logged in to leave comments. Login now