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