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