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