##// END OF EJS Templates
revpair: restrict odd-range handling to top-level x:y expression (issue4774)...
Yuya Nishihara -
r26020:cc3a30ff default
parent child Browse files
Show More
@@ -1,1123 +1,1127 b''
1 1 # scmutil.py - Mercurial core utility functions
2 2 #
3 3 # Copyright 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 i18n import _
9 9 from mercurial.node import wdirrev
10 10 import util, error, osutil, revset, similar, encoding, phases
11 11 import pathutil
12 12 import match as matchmod
13 13 import os, errno, re, glob, tempfile, shutil, stat
14 14
15 15 if os.name == 'nt':
16 16 import scmwindows as scmplatform
17 17 else:
18 18 import scmposix as scmplatform
19 19
20 20 systemrcpath = scmplatform.systemrcpath
21 21 userrcpath = scmplatform.userrcpath
22 22
23 23 class status(tuple):
24 24 '''Named tuple with a list of files per status. The 'deleted', 'unknown'
25 25 and 'ignored' properties are only relevant to the working copy.
26 26 '''
27 27
28 28 __slots__ = ()
29 29
30 30 def __new__(cls, modified, added, removed, deleted, unknown, ignored,
31 31 clean):
32 32 return tuple.__new__(cls, (modified, added, removed, deleted, unknown,
33 33 ignored, clean))
34 34
35 35 @property
36 36 def modified(self):
37 37 '''files that have been modified'''
38 38 return self[0]
39 39
40 40 @property
41 41 def added(self):
42 42 '''files that have been added'''
43 43 return self[1]
44 44
45 45 @property
46 46 def removed(self):
47 47 '''files that have been removed'''
48 48 return self[2]
49 49
50 50 @property
51 51 def deleted(self):
52 52 '''files that are in the dirstate, but have been deleted from the
53 53 working copy (aka "missing")
54 54 '''
55 55 return self[3]
56 56
57 57 @property
58 58 def unknown(self):
59 59 '''files not in the dirstate that are not ignored'''
60 60 return self[4]
61 61
62 62 @property
63 63 def ignored(self):
64 64 '''files not in the dirstate that are ignored (by _dirignore())'''
65 65 return self[5]
66 66
67 67 @property
68 68 def clean(self):
69 69 '''files that have not been modified'''
70 70 return self[6]
71 71
72 72 def __repr__(self, *args, **kwargs):
73 73 return (('<status modified=%r, added=%r, removed=%r, deleted=%r, '
74 74 'unknown=%r, ignored=%r, clean=%r>') % self)
75 75
76 76 def itersubrepos(ctx1, ctx2):
77 77 """find subrepos in ctx1 or ctx2"""
78 78 # Create a (subpath, ctx) mapping where we prefer subpaths from
79 79 # ctx1. The subpaths from ctx2 are important when the .hgsub file
80 80 # has been modified (in ctx2) but not yet committed (in ctx1).
81 81 subpaths = dict.fromkeys(ctx2.substate, ctx2)
82 82 subpaths.update(dict.fromkeys(ctx1.substate, ctx1))
83 83
84 84 missing = set()
85 85
86 86 for subpath in ctx2.substate:
87 87 if subpath not in ctx1.substate:
88 88 del subpaths[subpath]
89 89 missing.add(subpath)
90 90
91 91 for subpath, ctx in sorted(subpaths.iteritems()):
92 92 yield subpath, ctx.sub(subpath)
93 93
94 94 # Yield an empty subrepo based on ctx1 for anything only in ctx2. That way,
95 95 # status and diff will have an accurate result when it does
96 96 # 'sub.{status|diff}(rev2)'. Otherwise, the ctx2 subrepo is compared
97 97 # against itself.
98 98 for subpath in missing:
99 99 yield subpath, ctx2.nullsub(subpath, ctx1)
100 100
101 101 def nochangesfound(ui, repo, excluded=None):
102 102 '''Report no changes for push/pull, excluded is None or a list of
103 103 nodes excluded from the push/pull.
104 104 '''
105 105 secretlist = []
106 106 if excluded:
107 107 for n in excluded:
108 108 if n not in repo:
109 109 # discovery should not have included the filtered revision,
110 110 # we have to explicitly exclude it until discovery is cleanup.
111 111 continue
112 112 ctx = repo[n]
113 113 if ctx.phase() >= phases.secret and not ctx.extinct():
114 114 secretlist.append(n)
115 115
116 116 if secretlist:
117 117 ui.status(_("no changes found (ignored %d secret changesets)\n")
118 118 % len(secretlist))
119 119 else:
120 120 ui.status(_("no changes found\n"))
121 121
122 122 def checknewlabel(repo, lbl, kind):
123 123 # Do not use the "kind" parameter in ui output.
124 124 # It makes strings difficult to translate.
125 125 if lbl in ['tip', '.', 'null']:
126 126 raise util.Abort(_("the name '%s' is reserved") % lbl)
127 127 for c in (':', '\0', '\n', '\r'):
128 128 if c in lbl:
129 129 raise util.Abort(_("%r cannot be used in a name") % c)
130 130 try:
131 131 int(lbl)
132 132 raise util.Abort(_("cannot use an integer as a name"))
133 133 except ValueError:
134 134 pass
135 135
136 136 def checkfilename(f):
137 137 '''Check that the filename f is an acceptable filename for a tracked file'''
138 138 if '\r' in f or '\n' in f:
139 139 raise util.Abort(_("'\\n' and '\\r' disallowed in filenames: %r") % f)
140 140
141 141 def checkportable(ui, f):
142 142 '''Check if filename f is portable and warn or abort depending on config'''
143 143 checkfilename(f)
144 144 abort, warn = checkportabilityalert(ui)
145 145 if abort or warn:
146 146 msg = util.checkwinfilename(f)
147 147 if msg:
148 148 msg = "%s: %r" % (msg, f)
149 149 if abort:
150 150 raise util.Abort(msg)
151 151 ui.warn(_("warning: %s\n") % msg)
152 152
153 153 def checkportabilityalert(ui):
154 154 '''check if the user's config requests nothing, a warning, or abort for
155 155 non-portable filenames'''
156 156 val = ui.config('ui', 'portablefilenames', 'warn')
157 157 lval = val.lower()
158 158 bval = util.parsebool(val)
159 159 abort = os.name == 'nt' or lval == 'abort'
160 160 warn = bval or lval == 'warn'
161 161 if bval is None and not (warn or abort or lval == 'ignore'):
162 162 raise error.ConfigError(
163 163 _("ui.portablefilenames value is invalid ('%s')") % val)
164 164 return abort, warn
165 165
166 166 class casecollisionauditor(object):
167 167 def __init__(self, ui, abort, dirstate):
168 168 self._ui = ui
169 169 self._abort = abort
170 170 allfiles = '\0'.join(dirstate._map)
171 171 self._loweredfiles = set(encoding.lower(allfiles).split('\0'))
172 172 self._dirstate = dirstate
173 173 # The purpose of _newfiles is so that we don't complain about
174 174 # case collisions if someone were to call this object with the
175 175 # same filename twice.
176 176 self._newfiles = set()
177 177
178 178 def __call__(self, f):
179 179 if f in self._newfiles:
180 180 return
181 181 fl = encoding.lower(f)
182 182 if fl in self._loweredfiles and f not in self._dirstate:
183 183 msg = _('possible case-folding collision for %s') % f
184 184 if self._abort:
185 185 raise util.Abort(msg)
186 186 self._ui.warn(_("warning: %s\n") % msg)
187 187 self._loweredfiles.add(fl)
188 188 self._newfiles.add(f)
189 189
190 190 def filteredhash(repo, maxrev):
191 191 """build hash of filtered revisions in the current repoview.
192 192
193 193 Multiple caches perform up-to-date validation by checking that the
194 194 tiprev and tipnode stored in the cache file match the current repository.
195 195 However, this is not sufficient for validating repoviews because the set
196 196 of revisions in the view may change without the repository tiprev and
197 197 tipnode changing.
198 198
199 199 This function hashes all the revs filtered from the view and returns
200 200 that SHA-1 digest.
201 201 """
202 202 cl = repo.changelog
203 203 if not cl.filteredrevs:
204 204 return None
205 205 key = None
206 206 revs = sorted(r for r in cl.filteredrevs if r <= maxrev)
207 207 if revs:
208 208 s = util.sha1()
209 209 for rev in revs:
210 210 s.update('%s;' % rev)
211 211 key = s.digest()
212 212 return key
213 213
214 214 class abstractvfs(object):
215 215 """Abstract base class; cannot be instantiated"""
216 216
217 217 def __init__(self, *args, **kwargs):
218 218 '''Prevent instantiation; don't call this from subclasses.'''
219 219 raise NotImplementedError('attempted instantiating ' + str(type(self)))
220 220
221 221 def tryread(self, path):
222 222 '''gracefully return an empty string for missing files'''
223 223 try:
224 224 return self.read(path)
225 225 except IOError as inst:
226 226 if inst.errno != errno.ENOENT:
227 227 raise
228 228 return ""
229 229
230 230 def tryreadlines(self, path, mode='rb'):
231 231 '''gracefully return an empty array for missing files'''
232 232 try:
233 233 return self.readlines(path, mode=mode)
234 234 except IOError as inst:
235 235 if inst.errno != errno.ENOENT:
236 236 raise
237 237 return []
238 238
239 239 def open(self, path, mode="r", text=False, atomictemp=False,
240 240 notindexed=False):
241 241 '''Open ``path`` file, which is relative to vfs root.
242 242
243 243 Newly created directories are marked as "not to be indexed by
244 244 the content indexing service", if ``notindexed`` is specified
245 245 for "write" mode access.
246 246 '''
247 247 self.open = self.__call__
248 248 return self.__call__(path, mode, text, atomictemp, notindexed)
249 249
250 250 def read(self, path):
251 251 fp = self(path, 'rb')
252 252 try:
253 253 return fp.read()
254 254 finally:
255 255 fp.close()
256 256
257 257 def readlines(self, path, mode='rb'):
258 258 fp = self(path, mode=mode)
259 259 try:
260 260 return fp.readlines()
261 261 finally:
262 262 fp.close()
263 263
264 264 def write(self, path, data):
265 265 fp = self(path, 'wb')
266 266 try:
267 267 return fp.write(data)
268 268 finally:
269 269 fp.close()
270 270
271 271 def writelines(self, path, data, mode='wb', notindexed=False):
272 272 fp = self(path, mode=mode, notindexed=notindexed)
273 273 try:
274 274 return fp.writelines(data)
275 275 finally:
276 276 fp.close()
277 277
278 278 def append(self, path, data):
279 279 fp = self(path, 'ab')
280 280 try:
281 281 return fp.write(data)
282 282 finally:
283 283 fp.close()
284 284
285 285 def basename(self, path):
286 286 """return base element of a path (as os.path.basename would do)
287 287
288 288 This exists to allow handling of strange encoding if needed."""
289 289 return os.path.basename(path)
290 290
291 291 def chmod(self, path, mode):
292 292 return os.chmod(self.join(path), mode)
293 293
294 294 def dirname(self, path):
295 295 """return dirname element of a path (as os.path.dirname would do)
296 296
297 297 This exists to allow handling of strange encoding if needed."""
298 298 return os.path.dirname(path)
299 299
300 300 def exists(self, path=None):
301 301 return os.path.exists(self.join(path))
302 302
303 303 def fstat(self, fp):
304 304 return util.fstat(fp)
305 305
306 306 def isdir(self, path=None):
307 307 return os.path.isdir(self.join(path))
308 308
309 309 def isfile(self, path=None):
310 310 return os.path.isfile(self.join(path))
311 311
312 312 def islink(self, path=None):
313 313 return os.path.islink(self.join(path))
314 314
315 315 def reljoin(self, *paths):
316 316 """join various elements of a path together (as os.path.join would do)
317 317
318 318 The vfs base is not injected so that path stay relative. This exists
319 319 to allow handling of strange encoding if needed."""
320 320 return os.path.join(*paths)
321 321
322 322 def split(self, path):
323 323 """split top-most element of a path (as os.path.split would do)
324 324
325 325 This exists to allow handling of strange encoding if needed."""
326 326 return os.path.split(path)
327 327
328 328 def lexists(self, path=None):
329 329 return os.path.lexists(self.join(path))
330 330
331 331 def lstat(self, path=None):
332 332 return os.lstat(self.join(path))
333 333
334 334 def listdir(self, path=None):
335 335 return os.listdir(self.join(path))
336 336
337 337 def makedir(self, path=None, notindexed=True):
338 338 return util.makedir(self.join(path), notindexed)
339 339
340 340 def makedirs(self, path=None, mode=None):
341 341 return util.makedirs(self.join(path), mode)
342 342
343 343 def makelock(self, info, path):
344 344 return util.makelock(info, self.join(path))
345 345
346 346 def mkdir(self, path=None):
347 347 return os.mkdir(self.join(path))
348 348
349 349 def mkstemp(self, suffix='', prefix='tmp', dir=None, text=False):
350 350 fd, name = tempfile.mkstemp(suffix=suffix, prefix=prefix,
351 351 dir=self.join(dir), text=text)
352 352 dname, fname = util.split(name)
353 353 if dir:
354 354 return fd, os.path.join(dir, fname)
355 355 else:
356 356 return fd, fname
357 357
358 358 def readdir(self, path=None, stat=None, skip=None):
359 359 return osutil.listdir(self.join(path), stat, skip)
360 360
361 361 def readlock(self, path):
362 362 return util.readlock(self.join(path))
363 363
364 364 def rename(self, src, dst):
365 365 return util.rename(self.join(src), self.join(dst))
366 366
367 367 def readlink(self, path):
368 368 return os.readlink(self.join(path))
369 369
370 370 def removedirs(self, path=None):
371 371 """Remove a leaf directory and all empty intermediate ones
372 372 """
373 373 return util.removedirs(self.join(path))
374 374
375 375 def rmtree(self, path=None, ignore_errors=False, forcibly=False):
376 376 """Remove a directory tree recursively
377 377
378 378 If ``forcibly``, this tries to remove READ-ONLY files, too.
379 379 """
380 380 if forcibly:
381 381 def onerror(function, path, excinfo):
382 382 if function is not os.remove:
383 383 raise
384 384 # read-only files cannot be unlinked under Windows
385 385 s = os.stat(path)
386 386 if (s.st_mode & stat.S_IWRITE) != 0:
387 387 raise
388 388 os.chmod(path, stat.S_IMODE(s.st_mode) | stat.S_IWRITE)
389 389 os.remove(path)
390 390 else:
391 391 onerror = None
392 392 return shutil.rmtree(self.join(path),
393 393 ignore_errors=ignore_errors, onerror=onerror)
394 394
395 395 def setflags(self, path, l, x):
396 396 return util.setflags(self.join(path), l, x)
397 397
398 398 def stat(self, path=None):
399 399 return os.stat(self.join(path))
400 400
401 401 def unlink(self, path=None):
402 402 return util.unlink(self.join(path))
403 403
404 404 def unlinkpath(self, path=None, ignoremissing=False):
405 405 return util.unlinkpath(self.join(path), ignoremissing)
406 406
407 407 def utime(self, path=None, t=None):
408 408 return os.utime(self.join(path), t)
409 409
410 410 def walk(self, path=None, onerror=None):
411 411 """Yield (dirpath, dirs, files) tuple for each directories under path
412 412
413 413 ``dirpath`` is relative one from the root of this vfs. This
414 414 uses ``os.sep`` as path separator, even you specify POSIX
415 415 style ``path``.
416 416
417 417 "The root of this vfs" is represented as empty ``dirpath``.
418 418 """
419 419 root = os.path.normpath(self.join(None))
420 420 # when dirpath == root, dirpath[prefixlen:] becomes empty
421 421 # because len(dirpath) < prefixlen.
422 422 prefixlen = len(pathutil.normasprefix(root))
423 423 for dirpath, dirs, files in os.walk(self.join(path), onerror=onerror):
424 424 yield (dirpath[prefixlen:], dirs, files)
425 425
426 426 class vfs(abstractvfs):
427 427 '''Operate files relative to a base directory
428 428
429 429 This class is used to hide the details of COW semantics and
430 430 remote file access from higher level code.
431 431 '''
432 432 def __init__(self, base, audit=True, expandpath=False, realpath=False):
433 433 if expandpath:
434 434 base = util.expandpath(base)
435 435 if realpath:
436 436 base = os.path.realpath(base)
437 437 self.base = base
438 438 self._setmustaudit(audit)
439 439 self.createmode = None
440 440 self._trustnlink = None
441 441
442 442 def _getmustaudit(self):
443 443 return self._audit
444 444
445 445 def _setmustaudit(self, onoff):
446 446 self._audit = onoff
447 447 if onoff:
448 448 self.audit = pathutil.pathauditor(self.base)
449 449 else:
450 450 self.audit = util.always
451 451
452 452 mustaudit = property(_getmustaudit, _setmustaudit)
453 453
454 454 @util.propertycache
455 455 def _cansymlink(self):
456 456 return util.checklink(self.base)
457 457
458 458 @util.propertycache
459 459 def _chmod(self):
460 460 return util.checkexec(self.base)
461 461
462 462 def _fixfilemode(self, name):
463 463 if self.createmode is None or not self._chmod:
464 464 return
465 465 os.chmod(name, self.createmode & 0o666)
466 466
467 467 def __call__(self, path, mode="r", text=False, atomictemp=False,
468 468 notindexed=False):
469 469 '''Open ``path`` file, which is relative to vfs root.
470 470
471 471 Newly created directories are marked as "not to be indexed by
472 472 the content indexing service", if ``notindexed`` is specified
473 473 for "write" mode access.
474 474 '''
475 475 if self._audit:
476 476 r = util.checkosfilename(path)
477 477 if r:
478 478 raise util.Abort("%s: %r" % (r, path))
479 479 self.audit(path)
480 480 f = self.join(path)
481 481
482 482 if not text and "b" not in mode:
483 483 mode += "b" # for that other OS
484 484
485 485 nlink = -1
486 486 if mode not in ('r', 'rb'):
487 487 dirname, basename = util.split(f)
488 488 # If basename is empty, then the path is malformed because it points
489 489 # to a directory. Let the posixfile() call below raise IOError.
490 490 if basename:
491 491 if atomictemp:
492 492 util.ensuredirs(dirname, self.createmode, notindexed)
493 493 return util.atomictempfile(f, mode, self.createmode)
494 494 try:
495 495 if 'w' in mode:
496 496 util.unlink(f)
497 497 nlink = 0
498 498 else:
499 499 # nlinks() may behave differently for files on Windows
500 500 # shares if the file is open.
501 501 fd = util.posixfile(f)
502 502 nlink = util.nlinks(f)
503 503 if nlink < 1:
504 504 nlink = 2 # force mktempcopy (issue1922)
505 505 fd.close()
506 506 except (OSError, IOError) as e:
507 507 if e.errno != errno.ENOENT:
508 508 raise
509 509 nlink = 0
510 510 util.ensuredirs(dirname, self.createmode, notindexed)
511 511 if nlink > 0:
512 512 if self._trustnlink is None:
513 513 self._trustnlink = nlink > 1 or util.checknlink(f)
514 514 if nlink > 1 or not self._trustnlink:
515 515 util.rename(util.mktempcopy(f), f)
516 516 fp = util.posixfile(f, mode)
517 517 if nlink == 0:
518 518 self._fixfilemode(f)
519 519 return fp
520 520
521 521 def symlink(self, src, dst):
522 522 self.audit(dst)
523 523 linkname = self.join(dst)
524 524 try:
525 525 os.unlink(linkname)
526 526 except OSError:
527 527 pass
528 528
529 529 util.ensuredirs(os.path.dirname(linkname), self.createmode)
530 530
531 531 if self._cansymlink:
532 532 try:
533 533 os.symlink(src, linkname)
534 534 except OSError as err:
535 535 raise OSError(err.errno, _('could not symlink to %r: %s') %
536 536 (src, err.strerror), linkname)
537 537 else:
538 538 self.write(dst, src)
539 539
540 540 def join(self, path, *insidef):
541 541 if path:
542 542 return os.path.join(self.base, path, *insidef)
543 543 else:
544 544 return self.base
545 545
546 546 opener = vfs
547 547
548 548 class auditvfs(object):
549 549 def __init__(self, vfs):
550 550 self.vfs = vfs
551 551
552 552 def _getmustaudit(self):
553 553 return self.vfs.mustaudit
554 554
555 555 def _setmustaudit(self, onoff):
556 556 self.vfs.mustaudit = onoff
557 557
558 558 mustaudit = property(_getmustaudit, _setmustaudit)
559 559
560 560 class filtervfs(abstractvfs, auditvfs):
561 561 '''Wrapper vfs for filtering filenames with a function.'''
562 562
563 563 def __init__(self, vfs, filter):
564 564 auditvfs.__init__(self, vfs)
565 565 self._filter = filter
566 566
567 567 def __call__(self, path, *args, **kwargs):
568 568 return self.vfs(self._filter(path), *args, **kwargs)
569 569
570 570 def join(self, path, *insidef):
571 571 if path:
572 572 return self.vfs.join(self._filter(self.vfs.reljoin(path, *insidef)))
573 573 else:
574 574 return self.vfs.join(path)
575 575
576 576 filteropener = filtervfs
577 577
578 578 class readonlyvfs(abstractvfs, auditvfs):
579 579 '''Wrapper vfs preventing any writing.'''
580 580
581 581 def __init__(self, vfs):
582 582 auditvfs.__init__(self, vfs)
583 583
584 584 def __call__(self, path, mode='r', *args, **kw):
585 585 if mode not in ('r', 'rb'):
586 586 raise util.Abort('this vfs is read only')
587 587 return self.vfs(path, mode, *args, **kw)
588 588
589 589
590 590 def walkrepos(path, followsym=False, seen_dirs=None, recurse=False):
591 591 '''yield every hg repository under path, always recursively.
592 592 The recurse flag will only control recursion into repo working dirs'''
593 593 def errhandler(err):
594 594 if err.filename == path:
595 595 raise err
596 596 samestat = getattr(os.path, 'samestat', None)
597 597 if followsym and samestat is not None:
598 598 def adddir(dirlst, dirname):
599 599 match = False
600 600 dirstat = os.stat(dirname)
601 601 for lstdirstat in dirlst:
602 602 if samestat(dirstat, lstdirstat):
603 603 match = True
604 604 break
605 605 if not match:
606 606 dirlst.append(dirstat)
607 607 return not match
608 608 else:
609 609 followsym = False
610 610
611 611 if (seen_dirs is None) and followsym:
612 612 seen_dirs = []
613 613 adddir(seen_dirs, path)
614 614 for root, dirs, files in os.walk(path, topdown=True, onerror=errhandler):
615 615 dirs.sort()
616 616 if '.hg' in dirs:
617 617 yield root # found a repository
618 618 qroot = os.path.join(root, '.hg', 'patches')
619 619 if os.path.isdir(os.path.join(qroot, '.hg')):
620 620 yield qroot # we have a patch queue repo here
621 621 if recurse:
622 622 # avoid recursing inside the .hg directory
623 623 dirs.remove('.hg')
624 624 else:
625 625 dirs[:] = [] # don't descend further
626 626 elif followsym:
627 627 newdirs = []
628 628 for d in dirs:
629 629 fname = os.path.join(root, d)
630 630 if adddir(seen_dirs, fname):
631 631 if os.path.islink(fname):
632 632 for hgname in walkrepos(fname, True, seen_dirs):
633 633 yield hgname
634 634 else:
635 635 newdirs.append(d)
636 636 dirs[:] = newdirs
637 637
638 638 def osrcpath():
639 639 '''return default os-specific hgrc search path'''
640 640 path = []
641 641 defaultpath = os.path.join(util.datapath, 'default.d')
642 642 if os.path.isdir(defaultpath):
643 643 for f, kind in osutil.listdir(defaultpath):
644 644 if f.endswith('.rc'):
645 645 path.append(os.path.join(defaultpath, f))
646 646 path.extend(systemrcpath())
647 647 path.extend(userrcpath())
648 648 path = [os.path.normpath(f) for f in path]
649 649 return path
650 650
651 651 _rcpath = None
652 652
653 653 def rcpath():
654 654 '''return hgrc search path. if env var HGRCPATH is set, use it.
655 655 for each item in path, if directory, use files ending in .rc,
656 656 else use item.
657 657 make HGRCPATH empty to only look in .hg/hgrc of current repo.
658 658 if no HGRCPATH, use default os-specific path.'''
659 659 global _rcpath
660 660 if _rcpath is None:
661 661 if 'HGRCPATH' in os.environ:
662 662 _rcpath = []
663 663 for p in os.environ['HGRCPATH'].split(os.pathsep):
664 664 if not p:
665 665 continue
666 666 p = util.expandpath(p)
667 667 if os.path.isdir(p):
668 668 for f, kind in osutil.listdir(p):
669 669 if f.endswith('.rc'):
670 670 _rcpath.append(os.path.join(p, f))
671 671 else:
672 672 _rcpath.append(p)
673 673 else:
674 674 _rcpath = osrcpath()
675 675 return _rcpath
676 676
677 677 def intrev(rev):
678 678 """Return integer for a given revision that can be used in comparison or
679 679 arithmetic operation"""
680 680 if rev is None:
681 681 return wdirrev
682 682 return rev
683 683
684 684 def revsingle(repo, revspec, default='.'):
685 685 if not revspec and revspec != 0:
686 686 return repo[default]
687 687
688 688 l = revrange(repo, [revspec])
689 689 if not l:
690 690 raise util.Abort(_('empty revision set'))
691 691 return repo[l.last()]
692 692
693 def _pairspec(revspec):
694 tree = revset.parse(revspec)
695 tree = revset.optimize(tree, True)[1] # fix up "x^:y" -> "(x^):y"
696 return tree and tree[0] in ('range', 'rangepre', 'rangepost', 'rangeall')
697
693 698 def revpair(repo, revs):
694 699 if not revs:
695 700 return repo.dirstate.p1(), None
696 701
697 702 l = revrange(repo, revs)
698 703
699 704 if not l:
700 705 first = second = None
701 706 elif l.isascending():
702 707 first = l.min()
703 708 second = l.max()
704 709 elif l.isdescending():
705 710 first = l.max()
706 711 second = l.min()
707 712 else:
708 713 first = l.first()
709 714 second = l.last()
710 715
711 716 if first is None:
712 717 raise util.Abort(_('empty revision range'))
713 718
714 if first == second and len(revs) == 1 and _revrangesep not in revs[0]:
719 # if top-level is range expression, the result must always be a pair
720 if first == second and len(revs) == 1 and not _pairspec(revs[0]):
715 721 return repo.lookup(first), None
716 722
717 723 return repo.lookup(first), repo.lookup(second)
718 724
719 _revrangesep = ':'
720
721 725 def revrange(repo, revs):
722 726 """Yield revision as strings from a list of revision specifications."""
723 727 allspecs = []
724 728 for spec in revs:
725 729 if isinstance(spec, int):
726 730 spec = revset.formatspec('rev(%d)', spec)
727 731 allspecs.append(spec)
728 732 m = revset.matchany(repo.ui, allspecs, repo)
729 733 return m(repo)
730 734
731 735 def expandpats(pats):
732 736 '''Expand bare globs when running on windows.
733 737 On posix we assume it already has already been done by sh.'''
734 738 if not util.expandglobs:
735 739 return list(pats)
736 740 ret = []
737 741 for kindpat in pats:
738 742 kind, pat = matchmod._patsplit(kindpat, None)
739 743 if kind is None:
740 744 try:
741 745 globbed = glob.glob(pat)
742 746 except re.error:
743 747 globbed = [pat]
744 748 if globbed:
745 749 ret.extend(globbed)
746 750 continue
747 751 ret.append(kindpat)
748 752 return ret
749 753
750 754 def matchandpats(ctx, pats=[], opts={}, globbed=False, default='relpath',
751 755 badfn=None):
752 756 '''Return a matcher and the patterns that were used.
753 757 The matcher will warn about bad matches, unless an alternate badfn callback
754 758 is provided.'''
755 759 if pats == ("",):
756 760 pats = []
757 761 if not globbed and default == 'relpath':
758 762 pats = expandpats(pats or [])
759 763
760 764 def bad(f, msg):
761 765 ctx.repo().ui.warn("%s: %s\n" % (m.rel(f), msg))
762 766
763 767 if badfn is None:
764 768 badfn = bad
765 769
766 770 m = ctx.match(pats, opts.get('include'), opts.get('exclude'),
767 771 default, listsubrepos=opts.get('subrepos'), badfn=badfn)
768 772
769 773 if m.always():
770 774 pats = []
771 775 return m, pats
772 776
773 777 def match(ctx, pats=[], opts={}, globbed=False, default='relpath', badfn=None):
774 778 '''Return a matcher that will warn about bad matches.'''
775 779 return matchandpats(ctx, pats, opts, globbed, default, badfn=badfn)[0]
776 780
777 781 def matchall(repo):
778 782 '''Return a matcher that will efficiently match everything.'''
779 783 return matchmod.always(repo.root, repo.getcwd())
780 784
781 785 def matchfiles(repo, files, badfn=None):
782 786 '''Return a matcher that will efficiently match exactly these files.'''
783 787 return matchmod.exact(repo.root, repo.getcwd(), files, badfn=badfn)
784 788
785 789 def addremove(repo, matcher, prefix, opts={}, dry_run=None, similarity=None):
786 790 m = matcher
787 791 if dry_run is None:
788 792 dry_run = opts.get('dry_run')
789 793 if similarity is None:
790 794 similarity = float(opts.get('similarity') or 0)
791 795
792 796 ret = 0
793 797 join = lambda f: os.path.join(prefix, f)
794 798
795 799 def matchessubrepo(matcher, subpath):
796 800 if matcher.exact(subpath):
797 801 return True
798 802 for f in matcher.files():
799 803 if f.startswith(subpath):
800 804 return True
801 805 return False
802 806
803 807 wctx = repo[None]
804 808 for subpath in sorted(wctx.substate):
805 809 if opts.get('subrepos') or matchessubrepo(m, subpath):
806 810 sub = wctx.sub(subpath)
807 811 try:
808 812 submatch = matchmod.narrowmatcher(subpath, m)
809 813 if sub.addremove(submatch, prefix, opts, dry_run, similarity):
810 814 ret = 1
811 815 except error.LookupError:
812 816 repo.ui.status(_("skipping missing subrepository: %s\n")
813 817 % join(subpath))
814 818
815 819 rejected = []
816 820 def badfn(f, msg):
817 821 if f in m.files():
818 822 m.bad(f, msg)
819 823 rejected.append(f)
820 824
821 825 badmatch = matchmod.badmatch(m, badfn)
822 826 added, unknown, deleted, removed, forgotten = _interestingfiles(repo,
823 827 badmatch)
824 828
825 829 unknownset = set(unknown + forgotten)
826 830 toprint = unknownset.copy()
827 831 toprint.update(deleted)
828 832 for abs in sorted(toprint):
829 833 if repo.ui.verbose or not m.exact(abs):
830 834 if abs in unknownset:
831 835 status = _('adding %s\n') % m.uipath(abs)
832 836 else:
833 837 status = _('removing %s\n') % m.uipath(abs)
834 838 repo.ui.status(status)
835 839
836 840 renames = _findrenames(repo, m, added + unknown, removed + deleted,
837 841 similarity)
838 842
839 843 if not dry_run:
840 844 _markchanges(repo, unknown + forgotten, deleted, renames)
841 845
842 846 for f in rejected:
843 847 if f in m.files():
844 848 return 1
845 849 return ret
846 850
847 851 def marktouched(repo, files, similarity=0.0):
848 852 '''Assert that files have somehow been operated upon. files are relative to
849 853 the repo root.'''
850 854 m = matchfiles(repo, files, badfn=lambda x, y: rejected.append(x))
851 855 rejected = []
852 856
853 857 added, unknown, deleted, removed, forgotten = _interestingfiles(repo, m)
854 858
855 859 if repo.ui.verbose:
856 860 unknownset = set(unknown + forgotten)
857 861 toprint = unknownset.copy()
858 862 toprint.update(deleted)
859 863 for abs in sorted(toprint):
860 864 if abs in unknownset:
861 865 status = _('adding %s\n') % abs
862 866 else:
863 867 status = _('removing %s\n') % abs
864 868 repo.ui.status(status)
865 869
866 870 renames = _findrenames(repo, m, added + unknown, removed + deleted,
867 871 similarity)
868 872
869 873 _markchanges(repo, unknown + forgotten, deleted, renames)
870 874
871 875 for f in rejected:
872 876 if f in m.files():
873 877 return 1
874 878 return 0
875 879
876 880 def _interestingfiles(repo, matcher):
877 881 '''Walk dirstate with matcher, looking for files that addremove would care
878 882 about.
879 883
880 884 This is different from dirstate.status because it doesn't care about
881 885 whether files are modified or clean.'''
882 886 added, unknown, deleted, removed, forgotten = [], [], [], [], []
883 887 audit_path = pathutil.pathauditor(repo.root)
884 888
885 889 ctx = repo[None]
886 890 dirstate = repo.dirstate
887 891 walkresults = dirstate.walk(matcher, sorted(ctx.substate), True, False,
888 892 full=False)
889 893 for abs, st in walkresults.iteritems():
890 894 dstate = dirstate[abs]
891 895 if dstate == '?' and audit_path.check(abs):
892 896 unknown.append(abs)
893 897 elif dstate != 'r' and not st:
894 898 deleted.append(abs)
895 899 elif dstate == 'r' and st:
896 900 forgotten.append(abs)
897 901 # for finding renames
898 902 elif dstate == 'r' and not st:
899 903 removed.append(abs)
900 904 elif dstate == 'a':
901 905 added.append(abs)
902 906
903 907 return added, unknown, deleted, removed, forgotten
904 908
905 909 def _findrenames(repo, matcher, added, removed, similarity):
906 910 '''Find renames from removed files to added ones.'''
907 911 renames = {}
908 912 if similarity > 0:
909 913 for old, new, score in similar.findrenames(repo, added, removed,
910 914 similarity):
911 915 if (repo.ui.verbose or not matcher.exact(old)
912 916 or not matcher.exact(new)):
913 917 repo.ui.status(_('recording removal of %s as rename to %s '
914 918 '(%d%% similar)\n') %
915 919 (matcher.rel(old), matcher.rel(new),
916 920 score * 100))
917 921 renames[new] = old
918 922 return renames
919 923
920 924 def _markchanges(repo, unknown, deleted, renames):
921 925 '''Marks the files in unknown as added, the files in deleted as removed,
922 926 and the files in renames as copied.'''
923 927 wctx = repo[None]
924 928 wlock = repo.wlock()
925 929 try:
926 930 wctx.forget(deleted)
927 931 wctx.add(unknown)
928 932 for new, old in renames.iteritems():
929 933 wctx.copy(old, new)
930 934 finally:
931 935 wlock.release()
932 936
933 937 def dirstatecopy(ui, repo, wctx, src, dst, dryrun=False, cwd=None):
934 938 """Update the dirstate to reflect the intent of copying src to dst. For
935 939 different reasons it might not end with dst being marked as copied from src.
936 940 """
937 941 origsrc = repo.dirstate.copied(src) or src
938 942 if dst == origsrc: # copying back a copy?
939 943 if repo.dirstate[dst] not in 'mn' and not dryrun:
940 944 repo.dirstate.normallookup(dst)
941 945 else:
942 946 if repo.dirstate[origsrc] == 'a' and origsrc == src:
943 947 if not ui.quiet:
944 948 ui.warn(_("%s has not been committed yet, so no copy "
945 949 "data will be stored for %s.\n")
946 950 % (repo.pathto(origsrc, cwd), repo.pathto(dst, cwd)))
947 951 if repo.dirstate[dst] in '?r' and not dryrun:
948 952 wctx.add([dst])
949 953 elif not dryrun:
950 954 wctx.copy(origsrc, dst)
951 955
952 956 def readrequires(opener, supported):
953 957 '''Reads and parses .hg/requires and checks if all entries found
954 958 are in the list of supported features.'''
955 959 requirements = set(opener.read("requires").splitlines())
956 960 missings = []
957 961 for r in requirements:
958 962 if r not in supported:
959 963 if not r or not r[0].isalnum():
960 964 raise error.RequirementError(_(".hg/requires file is corrupt"))
961 965 missings.append(r)
962 966 missings.sort()
963 967 if missings:
964 968 raise error.RequirementError(
965 969 _("repository requires features unknown to this Mercurial: %s")
966 970 % " ".join(missings),
967 971 hint=_("see http://mercurial.selenic.com/wiki/MissingRequirement"
968 972 " for more information"))
969 973 return requirements
970 974
971 975 def writerequires(opener, requirements):
972 976 reqfile = opener("requires", "w")
973 977 for r in sorted(requirements):
974 978 reqfile.write("%s\n" % r)
975 979 reqfile.close()
976 980
977 981 class filecachesubentry(object):
978 982 def __init__(self, path, stat):
979 983 self.path = path
980 984 self.cachestat = None
981 985 self._cacheable = None
982 986
983 987 if stat:
984 988 self.cachestat = filecachesubentry.stat(self.path)
985 989
986 990 if self.cachestat:
987 991 self._cacheable = self.cachestat.cacheable()
988 992 else:
989 993 # None means we don't know yet
990 994 self._cacheable = None
991 995
992 996 def refresh(self):
993 997 if self.cacheable():
994 998 self.cachestat = filecachesubentry.stat(self.path)
995 999
996 1000 def cacheable(self):
997 1001 if self._cacheable is not None:
998 1002 return self._cacheable
999 1003
1000 1004 # we don't know yet, assume it is for now
1001 1005 return True
1002 1006
1003 1007 def changed(self):
1004 1008 # no point in going further if we can't cache it
1005 1009 if not self.cacheable():
1006 1010 return True
1007 1011
1008 1012 newstat = filecachesubentry.stat(self.path)
1009 1013
1010 1014 # we may not know if it's cacheable yet, check again now
1011 1015 if newstat and self._cacheable is None:
1012 1016 self._cacheable = newstat.cacheable()
1013 1017
1014 1018 # check again
1015 1019 if not self._cacheable:
1016 1020 return True
1017 1021
1018 1022 if self.cachestat != newstat:
1019 1023 self.cachestat = newstat
1020 1024 return True
1021 1025 else:
1022 1026 return False
1023 1027
1024 1028 @staticmethod
1025 1029 def stat(path):
1026 1030 try:
1027 1031 return util.cachestat(path)
1028 1032 except OSError as e:
1029 1033 if e.errno != errno.ENOENT:
1030 1034 raise
1031 1035
1032 1036 class filecacheentry(object):
1033 1037 def __init__(self, paths, stat=True):
1034 1038 self._entries = []
1035 1039 for path in paths:
1036 1040 self._entries.append(filecachesubentry(path, stat))
1037 1041
1038 1042 def changed(self):
1039 1043 '''true if any entry has changed'''
1040 1044 for entry in self._entries:
1041 1045 if entry.changed():
1042 1046 return True
1043 1047 return False
1044 1048
1045 1049 def refresh(self):
1046 1050 for entry in self._entries:
1047 1051 entry.refresh()
1048 1052
1049 1053 class filecache(object):
1050 1054 '''A property like decorator that tracks files under .hg/ for updates.
1051 1055
1052 1056 Records stat info when called in _filecache.
1053 1057
1054 1058 On subsequent calls, compares old stat info with new info, and recreates the
1055 1059 object when any of the files changes, updating the new stat info in
1056 1060 _filecache.
1057 1061
1058 1062 Mercurial either atomic renames or appends for files under .hg,
1059 1063 so to ensure the cache is reliable we need the filesystem to be able
1060 1064 to tell us if a file has been replaced. If it can't, we fallback to
1061 1065 recreating the object on every call (essentially the same behaviour as
1062 1066 propertycache).
1063 1067
1064 1068 '''
1065 1069 def __init__(self, *paths):
1066 1070 self.paths = paths
1067 1071
1068 1072 def join(self, obj, fname):
1069 1073 """Used to compute the runtime path of a cached file.
1070 1074
1071 1075 Users should subclass filecache and provide their own version of this
1072 1076 function to call the appropriate join function on 'obj' (an instance
1073 1077 of the class that its member function was decorated).
1074 1078 """
1075 1079 return obj.join(fname)
1076 1080
1077 1081 def __call__(self, func):
1078 1082 self.func = func
1079 1083 self.name = func.__name__
1080 1084 return self
1081 1085
1082 1086 def __get__(self, obj, type=None):
1083 1087 # do we need to check if the file changed?
1084 1088 if self.name in obj.__dict__:
1085 1089 assert self.name in obj._filecache, self.name
1086 1090 return obj.__dict__[self.name]
1087 1091
1088 1092 entry = obj._filecache.get(self.name)
1089 1093
1090 1094 if entry:
1091 1095 if entry.changed():
1092 1096 entry.obj = self.func(obj)
1093 1097 else:
1094 1098 paths = [self.join(obj, path) for path in self.paths]
1095 1099
1096 1100 # We stat -before- creating the object so our cache doesn't lie if
1097 1101 # a writer modified between the time we read and stat
1098 1102 entry = filecacheentry(paths, True)
1099 1103 entry.obj = self.func(obj)
1100 1104
1101 1105 obj._filecache[self.name] = entry
1102 1106
1103 1107 obj.__dict__[self.name] = entry.obj
1104 1108 return entry.obj
1105 1109
1106 1110 def __set__(self, obj, value):
1107 1111 if self.name not in obj._filecache:
1108 1112 # we add an entry for the missing value because X in __dict__
1109 1113 # implies X in _filecache
1110 1114 paths = [self.join(obj, path) for path in self.paths]
1111 1115 ce = filecacheentry(paths, False)
1112 1116 obj._filecache[self.name] = ce
1113 1117 else:
1114 1118 ce = obj._filecache[self.name]
1115 1119
1116 1120 ce.obj = value # update cached copy
1117 1121 obj.__dict__[self.name] = value # update copy returned by obj.x
1118 1122
1119 1123 def __delete__(self, obj):
1120 1124 try:
1121 1125 del obj.__dict__[self.name]
1122 1126 except KeyError:
1123 1127 raise AttributeError(self.name)
@@ -1,103 +1,137 b''
1 1 Testing diff --change
2 2
3 3 $ hg init a
4 4 $ cd a
5 5
6 6 $ echo "first" > file.txt
7 7 $ hg add file.txt
8 8 $ hg commit -m 'first commit' # 0
9 9
10 10 $ echo "second" > file.txt
11 11 $ hg commit -m 'second commit' # 1
12 12
13 13 $ echo "third" > file.txt
14 14 $ hg commit -m 'third commit' # 2
15 15
16 16 $ hg diff --nodates --change 1
17 17 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
18 18 --- a/file.txt
19 19 +++ b/file.txt
20 20 @@ -1,1 +1,1 @@
21 21 -first
22 22 +second
23 23
24 24 $ hg diff --change e9b286083166
25 25 diff -r 4bb65dda5db4 -r e9b286083166 file.txt
26 26 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
27 27 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
28 28 @@ -1,1 +1,1 @@
29 29 -first
30 30 +second
31 31
32 32 $ cd ..
33 33
34 Test dumb revspecs (issue3474)
34 Test dumb revspecs: top-level "x:y", "x:", ":y" and ":" ranges should be handled
35 as pairs even if x == y, but not for "f(x:y)" nor "x::y" (issue3474, issue4774)
35 36
36 37 $ hg clone -q a dumbspec
37 38 $ cd dumbspec
38 39 $ echo "wdir" > file.txt
39 40
40 41 $ hg diff -r 2:2
42 $ hg diff -r 2:.
43 $ hg diff -r 2:
44 $ hg diff -r :0
45 $ hg diff -r '2:first(2:2)'
46 $ hg diff -r 'first(2:2)' --nodates
47 diff -r bf5ff72eb7e0 file.txt
48 --- a/file.txt
49 +++ b/file.txt
50 @@ -1,1 +1,1 @@
51 -third
52 +wdir
53 $ hg diff -r 2::2 --nodates
54 diff -r bf5ff72eb7e0 file.txt
55 --- a/file.txt
56 +++ b/file.txt
57 @@ -1,1 +1,1 @@
58 -third
59 +wdir
41 60 $ hg diff -r "2 and 1"
42 61 abort: empty revision range
43 62 [255]
44 63
45 64 $ cd ..
46 65
66 $ hg clone -qr0 a dumbspec-rev0
67 $ cd dumbspec-rev0
68 $ echo "wdir" > file.txt
69
70 $ hg diff -r :
71 $ hg diff -r 'first(:)' --nodates
72 diff -r 4bb65dda5db4 file.txt
73 --- a/file.txt
74 +++ b/file.txt
75 @@ -1,1 +1,1 @@
76 -first
77 +wdir
78
79 $ cd ..
80
47 81 Testing diff --change when merge:
48 82
49 83 $ cd a
50 84
51 85 $ for i in 1 2 3 4 5 6 7 8 9 10; do
52 86 > echo $i >> file.txt
53 87 > done
54 88 $ hg commit -m "lots of text" # 3
55 89
56 90 $ sed -e 's,^2$,x,' file.txt > file.txt.tmp
57 91 $ mv file.txt.tmp file.txt
58 92 $ hg commit -m "change 2 to x" # 4
59 93
60 94 $ hg up -r 3
61 95 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 96 $ sed -e 's,^8$,y,' file.txt > file.txt.tmp
63 97 $ mv file.txt.tmp file.txt
64 98 $ hg commit -m "change 8 to y"
65 99 created new head
66 100
67 101 $ hg up -C -r 4
68 102 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
69 103 $ hg merge -r 5
70 104 merging file.txt
71 105 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
72 106 (branch merge, don't forget to commit)
73 107 $ hg commit -m "merge 8 to y" # 6
74 108
75 109 $ hg diff --change 5
76 110 diff -r ae119d680c82 -r 9085c5c02e52 file.txt
77 111 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
78 112 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
79 113 @@ -6,6 +6,6 @@
80 114 5
81 115 6
82 116 7
83 117 -8
84 118 +y
85 119 9
86 120 10
87 121
88 122 must be similar to 'hg diff --change 5':
89 123
90 124 $ hg diff -c 6
91 125 diff -r 273b50f17c6d -r 979ca961fd2e file.txt
92 126 --- a/file.txt Thu Jan 01 00:00:00 1970 +0000
93 127 +++ b/file.txt Thu Jan 01 00:00:00 1970 +0000
94 128 @@ -6,6 +6,6 @@
95 129 5
96 130 6
97 131 7
98 132 -8
99 133 +y
100 134 9
101 135 10
102 136
103 137 $ cd ..
@@ -1,2163 +1,2169 b''
1 1 $ HGENCODING=utf-8
2 2 $ export HGENCODING
3 3 $ cat > testrevset.py << EOF
4 4 > import mercurial.revset
5 5 >
6 6 > baseset = mercurial.revset.baseset
7 7 >
8 8 > def r3232(repo, subset, x):
9 9 > """"simple revset that return [3,2,3,2]
10 10 >
11 11 > revisions duplicated on purpose.
12 12 > """
13 13 > if 3 not in subset:
14 14 > if 2 in subset:
15 15 > return baseset([2,2])
16 16 > return baseset()
17 17 > return baseset([3,3,2,2])
18 18 >
19 19 > mercurial.revset.symbols['r3232'] = r3232
20 20 > EOF
21 21 $ cat >> $HGRCPATH << EOF
22 22 > [extensions]
23 23 > testrevset=$TESTTMP/testrevset.py
24 24 > EOF
25 25
26 26 $ try() {
27 27 > hg debugrevspec --debug "$@"
28 28 > }
29 29
30 30 $ log() {
31 31 > hg log --template '{rev}\n' -r "$1"
32 32 > }
33 33
34 34 $ hg init repo
35 35 $ cd repo
36 36
37 37 $ echo a > a
38 38 $ hg branch a
39 39 marked working directory as branch a
40 40 (branches are permanent and global, did you want a bookmark?)
41 41 $ hg ci -Aqm0
42 42
43 43 $ echo b > b
44 44 $ hg branch b
45 45 marked working directory as branch b
46 46 $ hg ci -Aqm1
47 47
48 48 $ rm a
49 49 $ hg branch a-b-c-
50 50 marked working directory as branch a-b-c-
51 51 $ hg ci -Aqm2 -u Bob
52 52
53 53 $ hg log -r "extra('branch', 'a-b-c-')" --template '{rev}\n'
54 54 2
55 55 $ hg log -r "extra('branch')" --template '{rev}\n'
56 56 0
57 57 1
58 58 2
59 59 $ hg log -r "extra('branch', 're:a')" --template '{rev} {branch}\n'
60 60 0 a
61 61 2 a-b-c-
62 62
63 63 $ hg co 1
64 64 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
65 65 $ hg branch +a+b+c+
66 66 marked working directory as branch +a+b+c+
67 67 $ hg ci -Aqm3
68 68
69 69 $ hg co 2 # interleave
70 70 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
71 71 $ echo bb > b
72 72 $ hg branch -- -a-b-c-
73 73 marked working directory as branch -a-b-c-
74 74 $ hg ci -Aqm4 -d "May 12 2005"
75 75
76 76 $ hg co 3
77 77 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
78 78 $ hg branch !a/b/c/
79 79 marked working directory as branch !a/b/c/
80 80 $ hg ci -Aqm"5 bug"
81 81
82 82 $ hg merge 4
83 83 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
84 84 (branch merge, don't forget to commit)
85 85 $ hg branch _a_b_c_
86 86 marked working directory as branch _a_b_c_
87 87 $ hg ci -Aqm"6 issue619"
88 88
89 89 $ hg branch .a.b.c.
90 90 marked working directory as branch .a.b.c.
91 91 $ hg ci -Aqm7
92 92
93 93 $ hg branch all
94 94 marked working directory as branch all
95 95
96 96 $ hg co 4
97 97 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
98 98 $ hg branch Γ©
99 99 marked working directory as branch \xc3\xa9 (esc)
100 100 $ hg ci -Aqm9
101 101
102 102 $ hg tag -r6 1.0
103 103 $ hg bookmark -r6 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
104 104
105 105 $ hg clone --quiet -U -r 7 . ../remote1
106 106 $ hg clone --quiet -U -r 8 . ../remote2
107 107 $ echo "[paths]" >> .hg/hgrc
108 108 $ echo "default = ../remote1" >> .hg/hgrc
109 109
110 110 trivial
111 111
112 112 $ try 0:1
113 113 (range
114 114 ('symbol', '0')
115 115 ('symbol', '1'))
116 116 * set:
117 117 <spanset+ 0:1>
118 118 0
119 119 1
120 120 $ try --optimize :
121 121 (rangeall
122 122 None)
123 123 * optimized:
124 124 (range
125 125 ('string', '0')
126 126 ('string', 'tip'))
127 127 * set:
128 128 <spanset+ 0:9>
129 129 0
130 130 1
131 131 2
132 132 3
133 133 4
134 134 5
135 135 6
136 136 7
137 137 8
138 138 9
139 139 $ try 3::6
140 140 (dagrange
141 141 ('symbol', '3')
142 142 ('symbol', '6'))
143 143 * set:
144 144 <baseset [3, 5, 6]>
145 145 3
146 146 5
147 147 6
148 148 $ try '0|1|2'
149 149 (or
150 150 ('symbol', '0')
151 151 ('symbol', '1')
152 152 ('symbol', '2'))
153 153 * set:
154 154 <baseset [0, 1, 2]>
155 155 0
156 156 1
157 157 2
158 158
159 159 names that should work without quoting
160 160
161 161 $ try a
162 162 ('symbol', 'a')
163 163 * set:
164 164 <baseset [0]>
165 165 0
166 166 $ try b-a
167 167 (minus
168 168 ('symbol', 'b')
169 169 ('symbol', 'a'))
170 170 * set:
171 171 <filteredset
172 172 <baseset [1]>>
173 173 1
174 174 $ try _a_b_c_
175 175 ('symbol', '_a_b_c_')
176 176 * set:
177 177 <baseset [6]>
178 178 6
179 179 $ try _a_b_c_-a
180 180 (minus
181 181 ('symbol', '_a_b_c_')
182 182 ('symbol', 'a'))
183 183 * set:
184 184 <filteredset
185 185 <baseset [6]>>
186 186 6
187 187 $ try .a.b.c.
188 188 ('symbol', '.a.b.c.')
189 189 * set:
190 190 <baseset [7]>
191 191 7
192 192 $ try .a.b.c.-a
193 193 (minus
194 194 ('symbol', '.a.b.c.')
195 195 ('symbol', 'a'))
196 196 * set:
197 197 <filteredset
198 198 <baseset [7]>>
199 199 7
200 200
201 201 names that should be caught by fallback mechanism
202 202
203 203 $ try -- '-a-b-c-'
204 204 ('symbol', '-a-b-c-')
205 205 * set:
206 206 <baseset [4]>
207 207 4
208 208 $ log -a-b-c-
209 209 4
210 210 $ try '+a+b+c+'
211 211 ('symbol', '+a+b+c+')
212 212 * set:
213 213 <baseset [3]>
214 214 3
215 215 $ try '+a+b+c+:'
216 216 (rangepost
217 217 ('symbol', '+a+b+c+'))
218 218 * set:
219 219 <spanset+ 3:9>
220 220 3
221 221 4
222 222 5
223 223 6
224 224 7
225 225 8
226 226 9
227 227 $ try ':+a+b+c+'
228 228 (rangepre
229 229 ('symbol', '+a+b+c+'))
230 230 * set:
231 231 <spanset+ 0:3>
232 232 0
233 233 1
234 234 2
235 235 3
236 236 $ try -- '-a-b-c-:+a+b+c+'
237 237 (range
238 238 ('symbol', '-a-b-c-')
239 239 ('symbol', '+a+b+c+'))
240 240 * set:
241 241 <spanset- 3:4>
242 242 4
243 243 3
244 244 $ log '-a-b-c-:+a+b+c+'
245 245 4
246 246 3
247 247
248 248 $ try -- -a-b-c--a # complains
249 249 (minus
250 250 (minus
251 251 (minus
252 252 (negate
253 253 ('symbol', 'a'))
254 254 ('symbol', 'b'))
255 255 ('symbol', 'c'))
256 256 (negate
257 257 ('symbol', 'a')))
258 258 abort: unknown revision '-a'!
259 259 [255]
260 260 $ try Γ©
261 261 ('symbol', '\xc3\xa9')
262 262 * set:
263 263 <baseset [9]>
264 264 9
265 265
266 266 no quoting needed
267 267
268 268 $ log ::a-b-c-
269 269 0
270 270 1
271 271 2
272 272
273 273 quoting needed
274 274
275 275 $ try '"-a-b-c-"-a'
276 276 (minus
277 277 ('string', '-a-b-c-')
278 278 ('symbol', 'a'))
279 279 * set:
280 280 <filteredset
281 281 <baseset [4]>>
282 282 4
283 283
284 284 $ log '1 or 2'
285 285 1
286 286 2
287 287 $ log '1|2'
288 288 1
289 289 2
290 290 $ log '1 and 2'
291 291 $ log '1&2'
292 292 $ try '1&2|3' # precedence - and is higher
293 293 (or
294 294 (and
295 295 ('symbol', '1')
296 296 ('symbol', '2'))
297 297 ('symbol', '3'))
298 298 * set:
299 299 <addset
300 300 <baseset []>,
301 301 <baseset [3]>>
302 302 3
303 303 $ try '1|2&3'
304 304 (or
305 305 ('symbol', '1')
306 306 (and
307 307 ('symbol', '2')
308 308 ('symbol', '3')))
309 309 * set:
310 310 <addset
311 311 <baseset [1]>,
312 312 <baseset []>>
313 313 1
314 314 $ try '1&2&3' # associativity
315 315 (and
316 316 (and
317 317 ('symbol', '1')
318 318 ('symbol', '2'))
319 319 ('symbol', '3'))
320 320 * set:
321 321 <baseset []>
322 322 $ try '1|(2|3)'
323 323 (or
324 324 ('symbol', '1')
325 325 (group
326 326 (or
327 327 ('symbol', '2')
328 328 ('symbol', '3'))))
329 329 * set:
330 330 <addset
331 331 <baseset [1]>,
332 332 <baseset [2, 3]>>
333 333 1
334 334 2
335 335 3
336 336 $ log '1.0' # tag
337 337 6
338 338 $ log 'a' # branch
339 339 0
340 340 $ log '2785f51ee'
341 341 0
342 342 $ log 'date(2005)'
343 343 4
344 344 $ log 'date(this is a test)'
345 345 hg: parse error at 10: unexpected token: symbol
346 346 [255]
347 347 $ log 'date()'
348 348 hg: parse error: date requires a string
349 349 [255]
350 350 $ log 'date'
351 351 abort: unknown revision 'date'!
352 352 [255]
353 353 $ log 'date('
354 354 hg: parse error at 5: not a prefix: end
355 355 [255]
356 356 $ log 'date(tip)'
357 357 abort: invalid date: 'tip'
358 358 [255]
359 359 $ log '0:date'
360 360 abort: unknown revision 'date'!
361 361 [255]
362 362 $ log '::"date"'
363 363 abort: unknown revision 'date'!
364 364 [255]
365 365 $ hg book date -r 4
366 366 $ log '0:date'
367 367 0
368 368 1
369 369 2
370 370 3
371 371 4
372 372 $ log '::date'
373 373 0
374 374 1
375 375 2
376 376 4
377 377 $ log '::"date"'
378 378 0
379 379 1
380 380 2
381 381 4
382 382 $ log 'date(2005) and 1::'
383 383 4
384 384 $ hg book -d date
385 385
386 386 keyword arguments
387 387
388 388 $ log 'extra(branch, value=a)'
389 389 0
390 390
391 391 $ log 'extra(branch, a, b)'
392 392 hg: parse error: extra takes at most 2 arguments
393 393 [255]
394 394 $ log 'extra(a, label=b)'
395 395 hg: parse error: extra got multiple values for keyword argument 'label'
396 396 [255]
397 397 $ log 'extra(label=branch, default)'
398 398 hg: parse error: extra got an invalid argument
399 399 [255]
400 400 $ log 'extra(branch, foo+bar=baz)'
401 401 hg: parse error: extra got an invalid argument
402 402 [255]
403 403 $ log 'extra(unknown=branch)'
404 404 hg: parse error: extra got an unexpected keyword argument 'unknown'
405 405 [255]
406 406
407 407 $ try 'foo=bar|baz'
408 408 (keyvalue
409 409 ('symbol', 'foo')
410 410 (or
411 411 ('symbol', 'bar')
412 412 ('symbol', 'baz')))
413 413 hg: parse error: can't use a key-value pair in this context
414 414 [255]
415 415
416 416 Test that symbols only get parsed as functions if there's an opening
417 417 parenthesis.
418 418
419 419 $ hg book only -r 9
420 420 $ log 'only(only)' # Outer "only" is a function, inner "only" is the bookmark
421 421 8
422 422 9
423 423
424 424 ancestor can accept 0 or more arguments
425 425
426 426 $ log 'ancestor()'
427 427 $ log 'ancestor(1)'
428 428 1
429 429 $ log 'ancestor(4,5)'
430 430 1
431 431 $ log 'ancestor(4,5) and 4'
432 432 $ log 'ancestor(0,0,1,3)'
433 433 0
434 434 $ log 'ancestor(3,1,5,3,5,1)'
435 435 1
436 436 $ log 'ancestor(0,1,3,5)'
437 437 0
438 438 $ log 'ancestor(1,2,3,4,5)'
439 439 1
440 440
441 441 test ancestors
442 442
443 443 $ log 'ancestors(5)'
444 444 0
445 445 1
446 446 3
447 447 5
448 448 $ log 'ancestor(ancestors(5))'
449 449 0
450 450 $ log '::r3232()'
451 451 0
452 452 1
453 453 2
454 454 3
455 455
456 456 $ log 'author(bob)'
457 457 2
458 458 $ log 'author("re:bob|test")'
459 459 0
460 460 1
461 461 2
462 462 3
463 463 4
464 464 5
465 465 6
466 466 7
467 467 8
468 468 9
469 469 $ log 'branch(Γ©)'
470 470 8
471 471 9
472 472 $ log 'branch(a)'
473 473 0
474 474 $ hg log -r 'branch("re:a")' --template '{rev} {branch}\n'
475 475 0 a
476 476 2 a-b-c-
477 477 3 +a+b+c+
478 478 4 -a-b-c-
479 479 5 !a/b/c/
480 480 6 _a_b_c_
481 481 7 .a.b.c.
482 482 $ log 'children(ancestor(4,5))'
483 483 2
484 484 3
485 485 $ log 'closed()'
486 486 $ log 'contains(a)'
487 487 0
488 488 1
489 489 3
490 490 5
491 491 $ log 'contains("../repo/a")'
492 492 0
493 493 1
494 494 3
495 495 5
496 496 $ log 'desc(B)'
497 497 5
498 498 $ log 'descendants(2 or 3)'
499 499 2
500 500 3
501 501 4
502 502 5
503 503 6
504 504 7
505 505 8
506 506 9
507 507 $ log 'file("b*")'
508 508 1
509 509 4
510 510 $ log 'filelog("b")'
511 511 1
512 512 4
513 513 $ log 'filelog("../repo/b")'
514 514 1
515 515 4
516 516 $ log 'follow()'
517 517 0
518 518 1
519 519 2
520 520 4
521 521 8
522 522 9
523 523 $ log 'grep("issue\d+")'
524 524 6
525 525 $ try 'grep("(")' # invalid regular expression
526 526 (func
527 527 ('symbol', 'grep')
528 528 ('string', '('))
529 529 hg: parse error: invalid match pattern: unbalanced parenthesis
530 530 [255]
531 531 $ try 'grep("\bissue\d+")'
532 532 (func
533 533 ('symbol', 'grep')
534 534 ('string', '\x08issue\\d+'))
535 535 * set:
536 536 <filteredset
537 537 <fullreposet+ 0:9>>
538 538 $ try 'grep(r"\bissue\d+")'
539 539 (func
540 540 ('symbol', 'grep')
541 541 ('string', '\\bissue\\d+'))
542 542 * set:
543 543 <filteredset
544 544 <fullreposet+ 0:9>>
545 545 6
546 546 $ try 'grep(r"\")'
547 547 hg: parse error at 7: unterminated string
548 548 [255]
549 549 $ log 'head()'
550 550 0
551 551 1
552 552 2
553 553 3
554 554 4
555 555 5
556 556 6
557 557 7
558 558 9
559 559 $ log 'heads(6::)'
560 560 7
561 561 $ log 'keyword(issue)'
562 562 6
563 563 $ log 'keyword("test a")'
564 564 $ log 'limit(head(), 1)'
565 565 0
566 566 $ log 'matching(6)'
567 567 6
568 568 $ log 'matching(6:7, "phase parents user date branch summary files description substate")'
569 569 6
570 570 7
571 571
572 572 Testing min and max
573 573
574 574 max: simple
575 575
576 576 $ log 'max(contains(a))'
577 577 5
578 578
579 579 max: simple on unordered set)
580 580
581 581 $ log 'max((4+0+2+5+7) and contains(a))'
582 582 5
583 583
584 584 max: no result
585 585
586 586 $ log 'max(contains(stringthatdoesnotappearanywhere))'
587 587
588 588 max: no result on unordered set
589 589
590 590 $ log 'max((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
591 591
592 592 min: simple
593 593
594 594 $ log 'min(contains(a))'
595 595 0
596 596
597 597 min: simple on unordered set
598 598
599 599 $ log 'min((4+0+2+5+7) and contains(a))'
600 600 0
601 601
602 602 min: empty
603 603
604 604 $ log 'min(contains(stringthatdoesnotappearanywhere))'
605 605
606 606 min: empty on unordered set
607 607
608 608 $ log 'min((4+0+2+5+7) and contains(stringthatdoesnotappearanywhere))'
609 609
610 610
611 611 $ log 'merge()'
612 612 6
613 613 $ log 'branchpoint()'
614 614 1
615 615 4
616 616 $ log 'modifies(b)'
617 617 4
618 618 $ log 'modifies("path:b")'
619 619 4
620 620 $ log 'modifies("*")'
621 621 4
622 622 6
623 623 $ log 'modifies("set:modified()")'
624 624 4
625 625 $ log 'id(5)'
626 626 2
627 627 $ log 'only(9)'
628 628 8
629 629 9
630 630 $ log 'only(8)'
631 631 8
632 632 $ log 'only(9, 5)'
633 633 2
634 634 4
635 635 8
636 636 9
637 637 $ log 'only(7 + 9, 5 + 2)'
638 638 4
639 639 6
640 640 7
641 641 8
642 642 9
643 643
644 644 Test empty set input
645 645 $ log 'only(p2())'
646 646 $ log 'only(p1(), p2())'
647 647 0
648 648 1
649 649 2
650 650 4
651 651 8
652 652 9
653 653
654 654 Test '%' operator
655 655
656 656 $ log '9%'
657 657 8
658 658 9
659 659 $ log '9%5'
660 660 2
661 661 4
662 662 8
663 663 9
664 664 $ log '(7 + 9)%(5 + 2)'
665 665 4
666 666 6
667 667 7
668 668 8
669 669 9
670 670
671 671 Test opreand of '%' is optimized recursively (issue4670)
672 672
673 673 $ try --optimize '8:9-8%'
674 674 (onlypost
675 675 (minus
676 676 (range
677 677 ('symbol', '8')
678 678 ('symbol', '9'))
679 679 ('symbol', '8')))
680 680 * optimized:
681 681 (func
682 682 ('symbol', 'only')
683 683 (and
684 684 (range
685 685 ('symbol', '8')
686 686 ('symbol', '9'))
687 687 (not
688 688 ('symbol', '8'))))
689 689 * set:
690 690 <baseset+ [8, 9]>
691 691 8
692 692 9
693 693 $ try --optimize '(9)%(5)'
694 694 (only
695 695 (group
696 696 ('symbol', '9'))
697 697 (group
698 698 ('symbol', '5')))
699 699 * optimized:
700 700 (func
701 701 ('symbol', 'only')
702 702 (list
703 703 ('symbol', '9')
704 704 ('symbol', '5')))
705 705 * set:
706 706 <baseset+ [8, 9, 2, 4]>
707 707 2
708 708 4
709 709 8
710 710 9
711 711
712 712 Test the order of operations
713 713
714 714 $ log '7 + 9%5 + 2'
715 715 7
716 716 2
717 717 4
718 718 8
719 719 9
720 720
721 721 Test explicit numeric revision
722 722 $ log 'rev(-2)'
723 723 $ log 'rev(-1)'
724 724 -1
725 725 $ log 'rev(0)'
726 726 0
727 727 $ log 'rev(9)'
728 728 9
729 729 $ log 'rev(10)'
730 730 $ log 'rev(tip)'
731 731 hg: parse error: rev expects a number
732 732 [255]
733 733
734 734 Test hexadecimal revision
735 735 $ log 'id(2)'
736 736 abort: 00changelog.i@2: ambiguous identifier!
737 737 [255]
738 738 $ log 'id(23268)'
739 739 4
740 740 $ log 'id(2785f51eece)'
741 741 0
742 742 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532c)'
743 743 8
744 744 $ log 'id(d5d0dcbdc4a)'
745 745 $ log 'id(d5d0dcbdc4w)'
746 746 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532d)'
747 747 $ log 'id(d5d0dcbdc4d9ff5dbb2d336f32f0bb561c1a532q)'
748 748 $ log 'id(1.0)'
749 749 $ log 'id(xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)'
750 750
751 751 Test null revision
752 752 $ log '(null)'
753 753 -1
754 754 $ log '(null:0)'
755 755 -1
756 756 0
757 757 $ log '(0:null)'
758 758 0
759 759 -1
760 760 $ log 'null::0'
761 761 -1
762 762 0
763 763 $ log 'null:tip - 0:'
764 764 -1
765 765 $ log 'null: and null::' | head -1
766 766 -1
767 767 $ log 'null: or 0:' | head -2
768 768 -1
769 769 0
770 770 $ log 'ancestors(null)'
771 771 -1
772 772 $ log 'reverse(null:)' | tail -2
773 773 0
774 774 -1
775 775 BROKEN: should be '-1'
776 776 $ log 'first(null:)'
777 777 BROKEN: should be '-1'
778 778 $ log 'min(null:)'
779 779 $ log 'tip:null and all()' | tail -2
780 780 1
781 781 0
782 782
783 783 Test working-directory revision
784 784 $ hg debugrevspec 'wdir()'
785 785 2147483647
786 786 $ hg debugrevspec 'tip or wdir()'
787 787 9
788 788 2147483647
789 789 $ hg debugrevspec '0:tip and wdir()'
790 790 $ log '0:wdir()' | tail -3
791 791 8
792 792 9
793 793 2147483647
794 794 $ log 'wdir():0' | head -3
795 795 2147483647
796 796 9
797 797 8
798 798 $ log 'wdir():wdir()'
799 799 2147483647
800 800 $ log '(all() + wdir()) & min(. + wdir())'
801 801 9
802 802 $ log '(all() + wdir()) & max(. + wdir())'
803 803 2147483647
804 804 $ log '(all() + wdir()) & first(wdir() + .)'
805 805 2147483647
806 806 $ log '(all() + wdir()) & last(. + wdir())'
807 807 2147483647
808 808
809 809 $ log 'outgoing()'
810 810 8
811 811 9
812 812 $ log 'outgoing("../remote1")'
813 813 8
814 814 9
815 815 $ log 'outgoing("../remote2")'
816 816 3
817 817 5
818 818 6
819 819 7
820 820 9
821 821 $ log 'p1(merge())'
822 822 5
823 823 $ log 'p2(merge())'
824 824 4
825 825 $ log 'parents(merge())'
826 826 4
827 827 5
828 828 $ log 'p1(branchpoint())'
829 829 0
830 830 2
831 831 $ log 'p2(branchpoint())'
832 832 $ log 'parents(branchpoint())'
833 833 0
834 834 2
835 835 $ log 'removes(a)'
836 836 2
837 837 6
838 838 $ log 'roots(all())'
839 839 0
840 840 $ log 'reverse(2 or 3 or 4 or 5)'
841 841 5
842 842 4
843 843 3
844 844 2
845 845 $ log 'reverse(all())'
846 846 9
847 847 8
848 848 7
849 849 6
850 850 5
851 851 4
852 852 3
853 853 2
854 854 1
855 855 0
856 856 $ log 'reverse(all()) & filelog(b)'
857 857 4
858 858 1
859 859 $ log 'rev(5)'
860 860 5
861 861 $ log 'sort(limit(reverse(all()), 3))'
862 862 7
863 863 8
864 864 9
865 865 $ log 'sort(2 or 3 or 4 or 5, date)'
866 866 2
867 867 3
868 868 5
869 869 4
870 870 $ log 'tagged()'
871 871 6
872 872 $ log 'tag()'
873 873 6
874 874 $ log 'tag(1.0)'
875 875 6
876 876 $ log 'tag(tip)'
877 877 9
878 878
879 879 test sort revset
880 880 --------------------------------------------
881 881
882 882 test when adding two unordered revsets
883 883
884 884 $ log 'sort(keyword(issue) or modifies(b))'
885 885 4
886 886 6
887 887
888 888 test when sorting a reversed collection in the same way it is
889 889
890 890 $ log 'sort(reverse(all()), -rev)'
891 891 9
892 892 8
893 893 7
894 894 6
895 895 5
896 896 4
897 897 3
898 898 2
899 899 1
900 900 0
901 901
902 902 test when sorting a reversed collection
903 903
904 904 $ log 'sort(reverse(all()), rev)'
905 905 0
906 906 1
907 907 2
908 908 3
909 909 4
910 910 5
911 911 6
912 912 7
913 913 8
914 914 9
915 915
916 916
917 917 test sorting two sorted collections in different orders
918 918
919 919 $ log 'sort(outgoing() or reverse(removes(a)), rev)'
920 920 2
921 921 6
922 922 8
923 923 9
924 924
925 925 test sorting two sorted collections in different orders backwards
926 926
927 927 $ log 'sort(outgoing() or reverse(removes(a)), -rev)'
928 928 9
929 929 8
930 930 6
931 931 2
932 932
933 933 test subtracting something from an addset
934 934
935 935 $ log '(outgoing() or removes(a)) - removes(a)'
936 936 8
937 937 9
938 938
939 939 test intersecting something with an addset
940 940
941 941 $ log 'parents(outgoing() or removes(a))'
942 942 1
943 943 4
944 944 5
945 945 8
946 946
947 947 test that `or` operation combines elements in the right order:
948 948
949 949 $ log '3:4 or 2:5'
950 950 3
951 951 4
952 952 2
953 953 5
954 954 $ log '3:4 or 5:2'
955 955 3
956 956 4
957 957 5
958 958 2
959 959 $ log 'sort(3:4 or 2:5)'
960 960 2
961 961 3
962 962 4
963 963 5
964 964 $ log 'sort(3:4 or 5:2)'
965 965 2
966 966 3
967 967 4
968 968 5
969 969
970 970 test that more than one `-r`s are combined in the right order and deduplicated:
971 971
972 972 $ hg log -T '{rev}\n' -r 3 -r 3 -r 4 -r 5:2 -r 'ancestors(4)'
973 973 3
974 974 4
975 975 5
976 976 2
977 977 0
978 978 1
979 979
980 980 test that `or` operation skips duplicated revisions from right-hand side
981 981
982 982 $ try 'reverse(1::5) or ancestors(4)'
983 983 (or
984 984 (func
985 985 ('symbol', 'reverse')
986 986 (dagrange
987 987 ('symbol', '1')
988 988 ('symbol', '5')))
989 989 (func
990 990 ('symbol', 'ancestors')
991 991 ('symbol', '4')))
992 992 * set:
993 993 <addset
994 994 <baseset [5, 3, 1]>,
995 995 <generatorset+>>
996 996 5
997 997 3
998 998 1
999 999 0
1000 1000 2
1001 1001 4
1002 1002 $ try 'sort(ancestors(4) or reverse(1::5))'
1003 1003 (func
1004 1004 ('symbol', 'sort')
1005 1005 (or
1006 1006 (func
1007 1007 ('symbol', 'ancestors')
1008 1008 ('symbol', '4'))
1009 1009 (func
1010 1010 ('symbol', 'reverse')
1011 1011 (dagrange
1012 1012 ('symbol', '1')
1013 1013 ('symbol', '5')))))
1014 1014 * set:
1015 1015 <addset+
1016 1016 <generatorset+>,
1017 1017 <baseset [5, 3, 1]>>
1018 1018 0
1019 1019 1
1020 1020 2
1021 1021 3
1022 1022 4
1023 1023 5
1024 1024
1025 1025 test optimization of trivial `or` operation
1026 1026
1027 1027 $ try --optimize '0|(1)|"2"|-2|tip|null'
1028 1028 (or
1029 1029 ('symbol', '0')
1030 1030 (group
1031 1031 ('symbol', '1'))
1032 1032 ('string', '2')
1033 1033 (negate
1034 1034 ('symbol', '2'))
1035 1035 ('symbol', 'tip')
1036 1036 ('symbol', 'null'))
1037 1037 * optimized:
1038 1038 (func
1039 1039 ('symbol', '_list')
1040 1040 ('string', '0\x001\x002\x00-2\x00tip\x00null'))
1041 1041 * set:
1042 1042 <baseset [0, 1, 2, 8, 9, -1]>
1043 1043 0
1044 1044 1
1045 1045 2
1046 1046 8
1047 1047 9
1048 1048 -1
1049 1049
1050 1050 $ try --optimize '0|1|2:3'
1051 1051 (or
1052 1052 ('symbol', '0')
1053 1053 ('symbol', '1')
1054 1054 (range
1055 1055 ('symbol', '2')
1056 1056 ('symbol', '3')))
1057 1057 * optimized:
1058 1058 (or
1059 1059 (func
1060 1060 ('symbol', '_list')
1061 1061 ('string', '0\x001'))
1062 1062 (range
1063 1063 ('symbol', '2')
1064 1064 ('symbol', '3')))
1065 1065 * set:
1066 1066 <addset
1067 1067 <baseset [0, 1]>,
1068 1068 <spanset+ 2:3>>
1069 1069 0
1070 1070 1
1071 1071 2
1072 1072 3
1073 1073
1074 1074 $ try --optimize '0:1|2|3:4|5|6'
1075 1075 (or
1076 1076 (range
1077 1077 ('symbol', '0')
1078 1078 ('symbol', '1'))
1079 1079 ('symbol', '2')
1080 1080 (range
1081 1081 ('symbol', '3')
1082 1082 ('symbol', '4'))
1083 1083 ('symbol', '5')
1084 1084 ('symbol', '6'))
1085 1085 * optimized:
1086 1086 (or
1087 1087 (range
1088 1088 ('symbol', '0')
1089 1089 ('symbol', '1'))
1090 1090 ('symbol', '2')
1091 1091 (range
1092 1092 ('symbol', '3')
1093 1093 ('symbol', '4'))
1094 1094 (func
1095 1095 ('symbol', '_list')
1096 1096 ('string', '5\x006')))
1097 1097 * set:
1098 1098 <addset
1099 1099 <addset
1100 1100 <spanset+ 0:1>,
1101 1101 <baseset [2]>>,
1102 1102 <addset
1103 1103 <spanset+ 3:4>,
1104 1104 <baseset [5, 6]>>>
1105 1105 0
1106 1106 1
1107 1107 2
1108 1108 3
1109 1109 4
1110 1110 5
1111 1111 6
1112 1112
1113 1113 test that `_list` should be narrowed by provided `subset`
1114 1114
1115 1115 $ log '0:2 and (null|1|2|3)'
1116 1116 1
1117 1117 2
1118 1118
1119 1119 test that `_list` should remove duplicates
1120 1120
1121 1121 $ log '0|1|2|1|2|-1|tip'
1122 1122 0
1123 1123 1
1124 1124 2
1125 1125 9
1126 1126
1127 1127 test unknown revision in `_list`
1128 1128
1129 1129 $ log '0|unknown'
1130 1130 abort: unknown revision 'unknown'!
1131 1131 [255]
1132 1132
1133 1133 test integer range in `_list`
1134 1134
1135 1135 $ log '-1|-10'
1136 1136 9
1137 1137 0
1138 1138
1139 1139 $ log '-10|-11'
1140 1140 abort: unknown revision '-11'!
1141 1141 [255]
1142 1142
1143 1143 $ log '9|10'
1144 1144 abort: unknown revision '10'!
1145 1145 [255]
1146 1146
1147 1147 test '0000' != '0' in `_list`
1148 1148
1149 1149 $ log '0|0000'
1150 1150 0
1151 1151 -1
1152 1152
1153 1153 test that chained `or` operations make balanced addsets
1154 1154
1155 1155 $ try '0:1|1:2|2:3|3:4|4:5'
1156 1156 (or
1157 1157 (range
1158 1158 ('symbol', '0')
1159 1159 ('symbol', '1'))
1160 1160 (range
1161 1161 ('symbol', '1')
1162 1162 ('symbol', '2'))
1163 1163 (range
1164 1164 ('symbol', '2')
1165 1165 ('symbol', '3'))
1166 1166 (range
1167 1167 ('symbol', '3')
1168 1168 ('symbol', '4'))
1169 1169 (range
1170 1170 ('symbol', '4')
1171 1171 ('symbol', '5')))
1172 1172 * set:
1173 1173 <addset
1174 1174 <addset
1175 1175 <spanset+ 0:1>,
1176 1176 <spanset+ 1:2>>,
1177 1177 <addset
1178 1178 <spanset+ 2:3>,
1179 1179 <addset
1180 1180 <spanset+ 3:4>,
1181 1181 <spanset+ 4:5>>>>
1182 1182 0
1183 1183 1
1184 1184 2
1185 1185 3
1186 1186 4
1187 1187 5
1188 1188
1189 1189 no crash by empty group "()" while optimizing `or` operations
1190 1190
1191 1191 $ try --optimize '0|()'
1192 1192 (or
1193 1193 ('symbol', '0')
1194 1194 (group
1195 1195 None))
1196 1196 * optimized:
1197 1197 (or
1198 1198 ('symbol', '0')
1199 1199 None)
1200 1200 hg: parse error: missing argument
1201 1201 [255]
1202 1202
1203 1203 test that chained `or` operations never eat up stack (issue4624)
1204 1204 (uses `0:1` instead of `0` to avoid future optimization of trivial revisions)
1205 1205
1206 1206 $ hg log -T '{rev}\n' -r "`python -c "print '|'.join(['0:1'] * 500)"`"
1207 1207 0
1208 1208 1
1209 1209
1210 1210 test that repeated `-r` options never eat up stack (issue4565)
1211 1211 (uses `-r 0::1` to avoid possible optimization at old-style parser)
1212 1212
1213 1213 $ hg log -T '{rev}\n' `python -c "for i in xrange(500): print '-r 0::1 ',"`
1214 1214 0
1215 1215 1
1216 1216
1217 1217 check that conversion to only works
1218 1218 $ try --optimize '::3 - ::1'
1219 1219 (minus
1220 1220 (dagrangepre
1221 1221 ('symbol', '3'))
1222 1222 (dagrangepre
1223 1223 ('symbol', '1')))
1224 1224 * optimized:
1225 1225 (func
1226 1226 ('symbol', 'only')
1227 1227 (list
1228 1228 ('symbol', '3')
1229 1229 ('symbol', '1')))
1230 1230 * set:
1231 1231 <baseset+ [3]>
1232 1232 3
1233 1233 $ try --optimize 'ancestors(1) - ancestors(3)'
1234 1234 (minus
1235 1235 (func
1236 1236 ('symbol', 'ancestors')
1237 1237 ('symbol', '1'))
1238 1238 (func
1239 1239 ('symbol', 'ancestors')
1240 1240 ('symbol', '3')))
1241 1241 * optimized:
1242 1242 (func
1243 1243 ('symbol', 'only')
1244 1244 (list
1245 1245 ('symbol', '1')
1246 1246 ('symbol', '3')))
1247 1247 * set:
1248 1248 <baseset+ []>
1249 1249 $ try --optimize 'not ::2 and ::6'
1250 1250 (and
1251 1251 (not
1252 1252 (dagrangepre
1253 1253 ('symbol', '2')))
1254 1254 (dagrangepre
1255 1255 ('symbol', '6')))
1256 1256 * optimized:
1257 1257 (func
1258 1258 ('symbol', 'only')
1259 1259 (list
1260 1260 ('symbol', '6')
1261 1261 ('symbol', '2')))
1262 1262 * set:
1263 1263 <baseset+ [3, 4, 5, 6]>
1264 1264 3
1265 1265 4
1266 1266 5
1267 1267 6
1268 1268 $ try --optimize 'ancestors(6) and not ancestors(4)'
1269 1269 (and
1270 1270 (func
1271 1271 ('symbol', 'ancestors')
1272 1272 ('symbol', '6'))
1273 1273 (not
1274 1274 (func
1275 1275 ('symbol', 'ancestors')
1276 1276 ('symbol', '4'))))
1277 1277 * optimized:
1278 1278 (func
1279 1279 ('symbol', 'only')
1280 1280 (list
1281 1281 ('symbol', '6')
1282 1282 ('symbol', '4')))
1283 1283 * set:
1284 1284 <baseset+ [3, 5, 6]>
1285 1285 3
1286 1286 5
1287 1287 6
1288 1288
1289 1289 no crash by empty group "()" while optimizing to "only()"
1290 1290
1291 1291 $ try --optimize '::1 and ()'
1292 1292 (and
1293 1293 (dagrangepre
1294 1294 ('symbol', '1'))
1295 1295 (group
1296 1296 None))
1297 1297 * optimized:
1298 1298 (and
1299 1299 None
1300 1300 (func
1301 1301 ('symbol', 'ancestors')
1302 1302 ('symbol', '1')))
1303 1303 hg: parse error: missing argument
1304 1304 [255]
1305 1305
1306 1306 we can use patterns when searching for tags
1307 1307
1308 1308 $ log 'tag("1..*")'
1309 1309 abort: tag '1..*' does not exist!
1310 1310 [255]
1311 1311 $ log 'tag("re:1..*")'
1312 1312 6
1313 1313 $ log 'tag("re:[0-9].[0-9]")'
1314 1314 6
1315 1315 $ log 'tag("literal:1.0")'
1316 1316 6
1317 1317 $ log 'tag("re:0..*")'
1318 1318
1319 1319 $ log 'tag(unknown)'
1320 1320 abort: tag 'unknown' does not exist!
1321 1321 [255]
1322 1322 $ log 'tag("re:unknown")'
1323 1323 $ log 'present(tag("unknown"))'
1324 1324 $ log 'present(tag("re:unknown"))'
1325 1325 $ log 'branch(unknown)'
1326 1326 abort: unknown revision 'unknown'!
1327 1327 [255]
1328 1328 $ log 'branch("re:unknown")'
1329 1329 $ log 'present(branch("unknown"))'
1330 1330 $ log 'present(branch("re:unknown"))'
1331 1331 $ log 'user(bob)'
1332 1332 2
1333 1333
1334 1334 $ log '4::8'
1335 1335 4
1336 1336 8
1337 1337 $ log '4:8'
1338 1338 4
1339 1339 5
1340 1340 6
1341 1341 7
1342 1342 8
1343 1343
1344 1344 $ log 'sort(!merge() & (modifies(b) | user(bob) | keyword(bug) | keyword(issue) & 1::9), "-date")'
1345 1345 4
1346 1346 2
1347 1347 5
1348 1348
1349 1349 $ log 'not 0 and 0:2'
1350 1350 1
1351 1351 2
1352 1352 $ log 'not 1 and 0:2'
1353 1353 0
1354 1354 2
1355 1355 $ log 'not 2 and 0:2'
1356 1356 0
1357 1357 1
1358 1358 $ log '(1 and 2)::'
1359 1359 $ log '(1 and 2):'
1360 1360 $ log '(1 and 2):3'
1361 1361 $ log 'sort(head(), -rev)'
1362 1362 9
1363 1363 7
1364 1364 6
1365 1365 5
1366 1366 4
1367 1367 3
1368 1368 2
1369 1369 1
1370 1370 0
1371 1371 $ log '4::8 - 8'
1372 1372 4
1373 1373 $ log 'matching(1 or 2 or 3) and (2 or 3 or 1)'
1374 1374 2
1375 1375 3
1376 1376 1
1377 1377
1378 1378 $ log 'named("unknown")'
1379 1379 abort: namespace 'unknown' does not exist!
1380 1380 [255]
1381 1381 $ log 'named("re:unknown")'
1382 1382 abort: no namespace exists that match 'unknown'!
1383 1383 [255]
1384 1384 $ log 'present(named("unknown"))'
1385 1385 $ log 'present(named("re:unknown"))'
1386 1386
1387 1387 $ log 'tag()'
1388 1388 6
1389 1389 $ log 'named("tags")'
1390 1390 6
1391 1391
1392 1392 issue2437
1393 1393
1394 1394 $ log '3 and p1(5)'
1395 1395 3
1396 1396 $ log '4 and p2(6)'
1397 1397 4
1398 1398 $ log '1 and parents(:2)'
1399 1399 1
1400 1400 $ log '2 and children(1:)'
1401 1401 2
1402 1402 $ log 'roots(all()) or roots(all())'
1403 1403 0
1404 1404 $ hg debugrevspec 'roots(all()) or roots(all())'
1405 1405 0
1406 1406 $ log 'heads(branch(Γ©)) or heads(branch(Γ©))'
1407 1407 9
1408 1408 $ log 'ancestors(8) and (heads(branch("-a-b-c-")) or heads(branch(Γ©)))'
1409 1409 4
1410 1410
1411 1411 issue2654: report a parse error if the revset was not completely parsed
1412 1412
1413 1413 $ log '1 OR 2'
1414 1414 hg: parse error at 2: invalid token
1415 1415 [255]
1416 1416
1417 1417 or operator should preserve ordering:
1418 1418 $ log 'reverse(2::4) or tip'
1419 1419 4
1420 1420 2
1421 1421 9
1422 1422
1423 1423 parentrevspec
1424 1424
1425 1425 $ log 'merge()^0'
1426 1426 6
1427 1427 $ log 'merge()^'
1428 1428 5
1429 1429 $ log 'merge()^1'
1430 1430 5
1431 1431 $ log 'merge()^2'
1432 1432 4
1433 1433 $ log 'merge()^^'
1434 1434 3
1435 1435 $ log 'merge()^1^'
1436 1436 3
1437 1437 $ log 'merge()^^^'
1438 1438 1
1439 1439
1440 1440 $ log 'merge()~0'
1441 1441 6
1442 1442 $ log 'merge()~1'
1443 1443 5
1444 1444 $ log 'merge()~2'
1445 1445 3
1446 1446 $ log 'merge()~2^1'
1447 1447 1
1448 1448 $ log 'merge()~3'
1449 1449 1
1450 1450
1451 1451 $ log '(-3:tip)^'
1452 1452 4
1453 1453 6
1454 1454 8
1455 1455
1456 1456 $ log 'tip^foo'
1457 1457 hg: parse error: ^ expects a number 0, 1, or 2
1458 1458 [255]
1459 1459
1460 1460 Bogus function gets suggestions
1461 1461 $ log 'add()'
1462 1462 hg: parse error: unknown identifier: add
1463 1463 (did you mean 'adds'?)
1464 1464 [255]
1465 1465 $ log 'added()'
1466 1466 hg: parse error: unknown identifier: added
1467 1467 (did you mean 'adds'?)
1468 1468 [255]
1469 1469 $ log 'remo()'
1470 1470 hg: parse error: unknown identifier: remo
1471 1471 (did you mean one of remote, removes?)
1472 1472 [255]
1473 1473 $ log 'babar()'
1474 1474 hg: parse error: unknown identifier: babar
1475 1475 [255]
1476 1476
1477 1477 Bogus function with a similar internal name doesn't suggest the internal name
1478 1478 $ log 'matches()'
1479 1479 hg: parse error: unknown identifier: matches
1480 1480 (did you mean 'matching'?)
1481 1481 [255]
1482 1482
1483 1483 Undocumented functions aren't suggested as similar either
1484 1484 $ log 'wdir2()'
1485 1485 hg: parse error: unknown identifier: wdir2
1486 1486 [255]
1487 1487
1488 1488 multiple revspecs
1489 1489
1490 1490 $ hg log -r 'tip~1:tip' -r 'tip~2:tip~1' --template '{rev}\n'
1491 1491 8
1492 1492 9
1493 1493 4
1494 1494 5
1495 1495 6
1496 1496 7
1497 1497
1498 1498 test usage in revpair (with "+")
1499 1499
1500 1500 (real pair)
1501 1501
1502 1502 $ hg diff -r 'tip^^' -r 'tip'
1503 1503 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1504 1504 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1505 1505 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1506 1506 @@ -0,0 +1,1 @@
1507 1507 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1508 1508 $ hg diff -r 'tip^^::tip'
1509 1509 diff -r 2326846efdab -r 24286f4ae135 .hgtags
1510 1510 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1511 1511 +++ b/.hgtags Thu Jan 01 00:00:00 1970 +0000
1512 1512 @@ -0,0 +1,1 @@
1513 1513 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1514 1514
1515 1515 (single rev)
1516 1516
1517 1517 $ hg diff -r 'tip^' -r 'tip^'
1518 $ hg diff -r 'tip^::tip^ or tip^'
1518 $ hg diff -r 'tip^:tip^'
1519 1519
1520 1520 (single rev that does not looks like a range)
1521 1521
1522 $ hg diff -r 'tip^::tip^ or tip^'
1523 diff -r d5d0dcbdc4d9 .hgtags
1524 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1525 +++ b/.hgtags * (glob)
1526 @@ -0,0 +1,1 @@
1527 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1522 1528 $ hg diff -r 'tip^ or tip^'
1523 1529 diff -r d5d0dcbdc4d9 .hgtags
1524 1530 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1525 1531 +++ b/.hgtags * (glob)
1526 1532 @@ -0,0 +1,1 @@
1527 1533 +e0cc66ef77e8b6f711815af4e001a6594fde3ba5 1.0
1528 1534
1529 1535 (no rev)
1530 1536
1531 1537 $ hg diff -r 'author("babar") or author("celeste")'
1532 1538 abort: empty revision range
1533 1539 [255]
1534 1540
1535 1541 aliases:
1536 1542
1537 1543 $ echo '[revsetalias]' >> .hg/hgrc
1538 1544 $ echo 'm = merge()' >> .hg/hgrc
1539 1545 (revset aliases can override builtin revsets)
1540 1546 $ echo 'p2($1) = p1($1)' >> .hg/hgrc
1541 1547 $ echo 'sincem = descendants(m)' >> .hg/hgrc
1542 1548 $ echo 'd($1) = reverse(sort($1, date))' >> .hg/hgrc
1543 1549 $ echo 'rs(ARG1, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1544 1550 $ echo 'rs4(ARG1, ARGA, ARGB, ARG2) = reverse(sort(ARG1, ARG2))' >> .hg/hgrc
1545 1551
1546 1552 $ try m
1547 1553 ('symbol', 'm')
1548 1554 (func
1549 1555 ('symbol', 'merge')
1550 1556 None)
1551 1557 * set:
1552 1558 <filteredset
1553 1559 <fullreposet+ 0:9>>
1554 1560 6
1555 1561
1556 1562 $ HGPLAIN=1
1557 1563 $ export HGPLAIN
1558 1564 $ try m
1559 1565 ('symbol', 'm')
1560 1566 abort: unknown revision 'm'!
1561 1567 [255]
1562 1568
1563 1569 $ HGPLAINEXCEPT=revsetalias
1564 1570 $ export HGPLAINEXCEPT
1565 1571 $ try m
1566 1572 ('symbol', 'm')
1567 1573 (func
1568 1574 ('symbol', 'merge')
1569 1575 None)
1570 1576 * set:
1571 1577 <filteredset
1572 1578 <fullreposet+ 0:9>>
1573 1579 6
1574 1580
1575 1581 $ unset HGPLAIN
1576 1582 $ unset HGPLAINEXCEPT
1577 1583
1578 1584 $ try 'p2(.)'
1579 1585 (func
1580 1586 ('symbol', 'p2')
1581 1587 ('symbol', '.'))
1582 1588 (func
1583 1589 ('symbol', 'p1')
1584 1590 ('symbol', '.'))
1585 1591 * set:
1586 1592 <baseset+ [8]>
1587 1593 8
1588 1594
1589 1595 $ HGPLAIN=1
1590 1596 $ export HGPLAIN
1591 1597 $ try 'p2(.)'
1592 1598 (func
1593 1599 ('symbol', 'p2')
1594 1600 ('symbol', '.'))
1595 1601 * set:
1596 1602 <baseset+ []>
1597 1603
1598 1604 $ HGPLAINEXCEPT=revsetalias
1599 1605 $ export HGPLAINEXCEPT
1600 1606 $ try 'p2(.)'
1601 1607 (func
1602 1608 ('symbol', 'p2')
1603 1609 ('symbol', '.'))
1604 1610 (func
1605 1611 ('symbol', 'p1')
1606 1612 ('symbol', '.'))
1607 1613 * set:
1608 1614 <baseset+ [8]>
1609 1615 8
1610 1616
1611 1617 $ unset HGPLAIN
1612 1618 $ unset HGPLAINEXCEPT
1613 1619
1614 1620 test alias recursion
1615 1621
1616 1622 $ try sincem
1617 1623 ('symbol', 'sincem')
1618 1624 (func
1619 1625 ('symbol', 'descendants')
1620 1626 (func
1621 1627 ('symbol', 'merge')
1622 1628 None))
1623 1629 * set:
1624 1630 <addset+
1625 1631 <filteredset
1626 1632 <fullreposet+ 0:9>>,
1627 1633 <generatorset+>>
1628 1634 6
1629 1635 7
1630 1636
1631 1637 test infinite recursion
1632 1638
1633 1639 $ echo 'recurse1 = recurse2' >> .hg/hgrc
1634 1640 $ echo 'recurse2 = recurse1' >> .hg/hgrc
1635 1641 $ try recurse1
1636 1642 ('symbol', 'recurse1')
1637 1643 hg: parse error: infinite expansion of revset alias "recurse1" detected
1638 1644 [255]
1639 1645
1640 1646 $ echo 'level1($1, $2) = $1 or $2' >> .hg/hgrc
1641 1647 $ echo 'level2($1, $2) = level1($2, $1)' >> .hg/hgrc
1642 1648 $ try "level2(level1(1, 2), 3)"
1643 1649 (func
1644 1650 ('symbol', 'level2')
1645 1651 (list
1646 1652 (func
1647 1653 ('symbol', 'level1')
1648 1654 (list
1649 1655 ('symbol', '1')
1650 1656 ('symbol', '2')))
1651 1657 ('symbol', '3')))
1652 1658 (or
1653 1659 ('symbol', '3')
1654 1660 (or
1655 1661 ('symbol', '1')
1656 1662 ('symbol', '2')))
1657 1663 * set:
1658 1664 <addset
1659 1665 <baseset [3]>,
1660 1666 <baseset [1, 2]>>
1661 1667 3
1662 1668 1
1663 1669 2
1664 1670
1665 1671 test nesting and variable passing
1666 1672
1667 1673 $ echo 'nested($1) = nested2($1)' >> .hg/hgrc
1668 1674 $ echo 'nested2($1) = nested3($1)' >> .hg/hgrc
1669 1675 $ echo 'nested3($1) = max($1)' >> .hg/hgrc
1670 1676 $ try 'nested(2:5)'
1671 1677 (func
1672 1678 ('symbol', 'nested')
1673 1679 (range
1674 1680 ('symbol', '2')
1675 1681 ('symbol', '5')))
1676 1682 (func
1677 1683 ('symbol', 'max')
1678 1684 (range
1679 1685 ('symbol', '2')
1680 1686 ('symbol', '5')))
1681 1687 * set:
1682 1688 <baseset [5]>
1683 1689 5
1684 1690
1685 1691 test chained `or` operations are flattened at parsing phase
1686 1692
1687 1693 $ echo 'chainedorops($1, $2, $3) = $1|$2|$3' >> .hg/hgrc
1688 1694 $ try 'chainedorops(0:1, 1:2, 2:3)'
1689 1695 (func
1690 1696 ('symbol', 'chainedorops')
1691 1697 (list
1692 1698 (list
1693 1699 (range
1694 1700 ('symbol', '0')
1695 1701 ('symbol', '1'))
1696 1702 (range
1697 1703 ('symbol', '1')
1698 1704 ('symbol', '2')))
1699 1705 (range
1700 1706 ('symbol', '2')
1701 1707 ('symbol', '3'))))
1702 1708 (or
1703 1709 (range
1704 1710 ('symbol', '0')
1705 1711 ('symbol', '1'))
1706 1712 (range
1707 1713 ('symbol', '1')
1708 1714 ('symbol', '2'))
1709 1715 (range
1710 1716 ('symbol', '2')
1711 1717 ('symbol', '3')))
1712 1718 * set:
1713 1719 <addset
1714 1720 <spanset+ 0:1>,
1715 1721 <addset
1716 1722 <spanset+ 1:2>,
1717 1723 <spanset+ 2:3>>>
1718 1724 0
1719 1725 1
1720 1726 2
1721 1727 3
1722 1728
1723 1729 test variable isolation, variable placeholders are rewritten as string
1724 1730 then parsed and matched again as string. Check they do not leak too
1725 1731 far away.
1726 1732
1727 1733 $ echo 'injectparamasstring = max("$1")' >> .hg/hgrc
1728 1734 $ echo 'callinjection($1) = descendants(injectparamasstring)' >> .hg/hgrc
1729 1735 $ try 'callinjection(2:5)'
1730 1736 (func
1731 1737 ('symbol', 'callinjection')
1732 1738 (range
1733 1739 ('symbol', '2')
1734 1740 ('symbol', '5')))
1735 1741 (func
1736 1742 ('symbol', 'descendants')
1737 1743 (func
1738 1744 ('symbol', 'max')
1739 1745 ('string', '$1')))
1740 1746 abort: unknown revision '$1'!
1741 1747 [255]
1742 1748
1743 1749 $ echo 'injectparamasstring2 = max(_aliasarg("$1"))' >> .hg/hgrc
1744 1750 $ echo 'callinjection2($1) = descendants(injectparamasstring2)' >> .hg/hgrc
1745 1751 $ try 'callinjection2(2:5)'
1746 1752 (func
1747 1753 ('symbol', 'callinjection2')
1748 1754 (range
1749 1755 ('symbol', '2')
1750 1756 ('symbol', '5')))
1751 1757 abort: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1752 1758 [255]
1753 1759 $ hg debugrevspec --debug --config revsetalias.anotherbadone='branch(' "tip"
1754 1760 ('symbol', 'tip')
1755 1761 warning: failed to parse the definition of revset alias "anotherbadone": at 7: not a prefix: end
1756 1762 warning: failed to parse the definition of revset alias "injectparamasstring2": unknown identifier: _aliasarg
1757 1763 * set:
1758 1764 <baseset [9]>
1759 1765 9
1760 1766 >>> data = file('.hg/hgrc', 'rb').read()
1761 1767 >>> file('.hg/hgrc', 'wb').write(data.replace('_aliasarg', ''))
1762 1768
1763 1769 $ try 'tip'
1764 1770 ('symbol', 'tip')
1765 1771 * set:
1766 1772 <baseset [9]>
1767 1773 9
1768 1774
1769 1775 $ hg debugrevspec --debug --config revsetalias.'bad name'='tip' "tip"
1770 1776 ('symbol', 'tip')
1771 1777 warning: failed to parse the declaration of revset alias "bad name": at 4: invalid token
1772 1778 * set:
1773 1779 <baseset [9]>
1774 1780 9
1775 1781 $ echo 'strictreplacing($1, $10) = $10 or desc("$1")' >> .hg/hgrc
1776 1782 $ try 'strictreplacing("foo", tip)'
1777 1783 (func
1778 1784 ('symbol', 'strictreplacing')
1779 1785 (list
1780 1786 ('string', 'foo')
1781 1787 ('symbol', 'tip')))
1782 1788 (or
1783 1789 ('symbol', 'tip')
1784 1790 (func
1785 1791 ('symbol', 'desc')
1786 1792 ('string', '$1')))
1787 1793 * set:
1788 1794 <addset
1789 1795 <baseset [9]>,
1790 1796 <filteredset
1791 1797 <fullreposet+ 0:9>>>
1792 1798 9
1793 1799
1794 1800 $ try 'd(2:5)'
1795 1801 (func
1796 1802 ('symbol', 'd')
1797 1803 (range
1798 1804 ('symbol', '2')
1799 1805 ('symbol', '5')))
1800 1806 (func
1801 1807 ('symbol', 'reverse')
1802 1808 (func
1803 1809 ('symbol', 'sort')
1804 1810 (list
1805 1811 (range
1806 1812 ('symbol', '2')
1807 1813 ('symbol', '5'))
1808 1814 ('symbol', 'date'))))
1809 1815 * set:
1810 1816 <baseset [4, 5, 3, 2]>
1811 1817 4
1812 1818 5
1813 1819 3
1814 1820 2
1815 1821 $ try 'rs(2 or 3, date)'
1816 1822 (func
1817 1823 ('symbol', 'rs')
1818 1824 (list
1819 1825 (or
1820 1826 ('symbol', '2')
1821 1827 ('symbol', '3'))
1822 1828 ('symbol', 'date')))
1823 1829 (func
1824 1830 ('symbol', 'reverse')
1825 1831 (func
1826 1832 ('symbol', 'sort')
1827 1833 (list
1828 1834 (or
1829 1835 ('symbol', '2')
1830 1836 ('symbol', '3'))
1831 1837 ('symbol', 'date'))))
1832 1838 * set:
1833 1839 <baseset [3, 2]>
1834 1840 3
1835 1841 2
1836 1842 $ try 'rs()'
1837 1843 (func
1838 1844 ('symbol', 'rs')
1839 1845 None)
1840 1846 hg: parse error: invalid number of arguments: 0
1841 1847 [255]
1842 1848 $ try 'rs(2)'
1843 1849 (func
1844 1850 ('symbol', 'rs')
1845 1851 ('symbol', '2'))
1846 1852 hg: parse error: invalid number of arguments: 1
1847 1853 [255]
1848 1854 $ try 'rs(2, data, 7)'
1849 1855 (func
1850 1856 ('symbol', 'rs')
1851 1857 (list
1852 1858 (list
1853 1859 ('symbol', '2')
1854 1860 ('symbol', 'data'))
1855 1861 ('symbol', '7')))
1856 1862 hg: parse error: invalid number of arguments: 3
1857 1863 [255]
1858 1864 $ try 'rs4(2 or 3, x, x, date)'
1859 1865 (func
1860 1866 ('symbol', 'rs4')
1861 1867 (list
1862 1868 (list
1863 1869 (list
1864 1870 (or
1865 1871 ('symbol', '2')
1866 1872 ('symbol', '3'))
1867 1873 ('symbol', 'x'))
1868 1874 ('symbol', 'x'))
1869 1875 ('symbol', 'date')))
1870 1876 (func
1871 1877 ('symbol', 'reverse')
1872 1878 (func
1873 1879 ('symbol', 'sort')
1874 1880 (list
1875 1881 (or
1876 1882 ('symbol', '2')
1877 1883 ('symbol', '3'))
1878 1884 ('symbol', 'date'))))
1879 1885 * set:
1880 1886 <baseset [3, 2]>
1881 1887 3
1882 1888 2
1883 1889
1884 1890 issue4553: check that revset aliases override existing hash prefix
1885 1891
1886 1892 $ hg log -qr e
1887 1893 6:e0cc66ef77e8
1888 1894
1889 1895 $ hg log -qr e --config revsetalias.e="all()"
1890 1896 0:2785f51eece5
1891 1897 1:d75937da8da0
1892 1898 2:5ed5505e9f1c
1893 1899 3:8528aa5637f2
1894 1900 4:2326846efdab
1895 1901 5:904fa392b941
1896 1902 6:e0cc66ef77e8
1897 1903 7:013af1973af4
1898 1904 8:d5d0dcbdc4d9
1899 1905 9:24286f4ae135
1900 1906
1901 1907 $ hg log -qr e: --config revsetalias.e="0"
1902 1908 0:2785f51eece5
1903 1909 1:d75937da8da0
1904 1910 2:5ed5505e9f1c
1905 1911 3:8528aa5637f2
1906 1912 4:2326846efdab
1907 1913 5:904fa392b941
1908 1914 6:e0cc66ef77e8
1909 1915 7:013af1973af4
1910 1916 8:d5d0dcbdc4d9
1911 1917 9:24286f4ae135
1912 1918
1913 1919 $ hg log -qr :e --config revsetalias.e="9"
1914 1920 0:2785f51eece5
1915 1921 1:d75937da8da0
1916 1922 2:5ed5505e9f1c
1917 1923 3:8528aa5637f2
1918 1924 4:2326846efdab
1919 1925 5:904fa392b941
1920 1926 6:e0cc66ef77e8
1921 1927 7:013af1973af4
1922 1928 8:d5d0dcbdc4d9
1923 1929 9:24286f4ae135
1924 1930
1925 1931 $ hg log -qr e:
1926 1932 6:e0cc66ef77e8
1927 1933 7:013af1973af4
1928 1934 8:d5d0dcbdc4d9
1929 1935 9:24286f4ae135
1930 1936
1931 1937 $ hg log -qr :e
1932 1938 0:2785f51eece5
1933 1939 1:d75937da8da0
1934 1940 2:5ed5505e9f1c
1935 1941 3:8528aa5637f2
1936 1942 4:2326846efdab
1937 1943 5:904fa392b941
1938 1944 6:e0cc66ef77e8
1939 1945
1940 1946 issue2549 - correct optimizations
1941 1947
1942 1948 $ log 'limit(1 or 2 or 3, 2) and not 2'
1943 1949 1
1944 1950 $ log 'max(1 or 2) and not 2'
1945 1951 $ log 'min(1 or 2) and not 1'
1946 1952 $ log 'last(1 or 2, 1) and not 2'
1947 1953
1948 1954 issue4289 - ordering of built-ins
1949 1955 $ hg log -M -q -r 3:2
1950 1956 3:8528aa5637f2
1951 1957 2:5ed5505e9f1c
1952 1958
1953 1959 test revsets started with 40-chars hash (issue3669)
1954 1960
1955 1961 $ ISSUE3669_TIP=`hg tip --template '{node}'`
1956 1962 $ hg log -r "${ISSUE3669_TIP}" --template '{rev}\n'
1957 1963 9
1958 1964 $ hg log -r "${ISSUE3669_TIP}^" --template '{rev}\n'
1959 1965 8
1960 1966
1961 1967 test or-ed indirect predicates (issue3775)
1962 1968
1963 1969 $ log '6 or 6^1' | sort
1964 1970 5
1965 1971 6
1966 1972 $ log '6^1 or 6' | sort
1967 1973 5
1968 1974 6
1969 1975 $ log '4 or 4~1' | sort
1970 1976 2
1971 1977 4
1972 1978 $ log '4~1 or 4' | sort
1973 1979 2
1974 1980 4
1975 1981 $ log '(0 or 2):(4 or 6) or 0 or 6' | sort
1976 1982 0
1977 1983 1
1978 1984 2
1979 1985 3
1980 1986 4
1981 1987 5
1982 1988 6
1983 1989 $ log '0 or 6 or (0 or 2):(4 or 6)' | sort
1984 1990 0
1985 1991 1
1986 1992 2
1987 1993 3
1988 1994 4
1989 1995 5
1990 1996 6
1991 1997
1992 1998 tests for 'remote()' predicate:
1993 1999 #. (csets in remote) (id) (remote)
1994 2000 1. less than local current branch "default"
1995 2001 2. same with local specified "default"
1996 2002 3. more than local specified specified
1997 2003
1998 2004 $ hg clone --quiet -U . ../remote3
1999 2005 $ cd ../remote3
2000 2006 $ hg update -q 7
2001 2007 $ echo r > r
2002 2008 $ hg ci -Aqm 10
2003 2009 $ log 'remote()'
2004 2010 7
2005 2011 $ log 'remote("a-b-c-")'
2006 2012 2
2007 2013 $ cd ../repo
2008 2014 $ log 'remote(".a.b.c.", "../remote3")'
2009 2015
2010 2016 tests for concatenation of strings/symbols by "##"
2011 2017
2012 2018 $ try "278 ## '5f5' ## 1ee ## 'ce5'"
2013 2019 (_concat
2014 2020 (_concat
2015 2021 (_concat
2016 2022 ('symbol', '278')
2017 2023 ('string', '5f5'))
2018 2024 ('symbol', '1ee'))
2019 2025 ('string', 'ce5'))
2020 2026 ('string', '2785f51eece5')
2021 2027 * set:
2022 2028 <baseset [0]>
2023 2029 0
2024 2030
2025 2031 $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc
2026 2032 $ try "cat4(278, '5f5', 1ee, 'ce5')"
2027 2033 (func
2028 2034 ('symbol', 'cat4')
2029 2035 (list
2030 2036 (list
2031 2037 (list
2032 2038 ('symbol', '278')
2033 2039 ('string', '5f5'))
2034 2040 ('symbol', '1ee'))
2035 2041 ('string', 'ce5')))
2036 2042 (_concat
2037 2043 (_concat
2038 2044 (_concat
2039 2045 ('symbol', '278')
2040 2046 ('string', '5f5'))
2041 2047 ('symbol', '1ee'))
2042 2048 ('string', 'ce5'))
2043 2049 ('string', '2785f51eece5')
2044 2050 * set:
2045 2051 <baseset [0]>
2046 2052 0
2047 2053
2048 2054 (check concatenation in alias nesting)
2049 2055
2050 2056 $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc
2051 2057 $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc
2052 2058 $ log "cat2x2(278, '5f5', 1ee, 'ce5')"
2053 2059 0
2054 2060
2055 2061 (check operator priority)
2056 2062
2057 2063 $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc
2058 2064 $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)"
2059 2065 0
2060 2066 4
2061 2067
2062 2068 $ cd ..
2063 2069
2064 2070 prepare repository that has "default" branches of multiple roots
2065 2071
2066 2072 $ hg init namedbranch
2067 2073 $ cd namedbranch
2068 2074
2069 2075 $ echo default0 >> a
2070 2076 $ hg ci -Aqm0
2071 2077 $ echo default1 >> a
2072 2078 $ hg ci -m1
2073 2079
2074 2080 $ hg branch -q stable
2075 2081 $ echo stable2 >> a
2076 2082 $ hg ci -m2
2077 2083 $ echo stable3 >> a
2078 2084 $ hg ci -m3
2079 2085
2080 2086 $ hg update -q null
2081 2087 $ echo default4 >> a
2082 2088 $ hg ci -Aqm4
2083 2089 $ echo default5 >> a
2084 2090 $ hg ci -m5
2085 2091
2086 2092 "null" revision belongs to "default" branch (issue4683)
2087 2093
2088 2094 $ log 'branch(null)'
2089 2095 0
2090 2096 1
2091 2097 4
2092 2098 5
2093 2099
2094 2100 "null" revision belongs to "default" branch, but it shouldn't appear in set
2095 2101 unless explicitly specified (issue4682)
2096 2102
2097 2103 $ log 'children(branch(default))'
2098 2104 1
2099 2105 2
2100 2106 5
2101 2107
2102 2108 $ cd ..
2103 2109
2104 2110 test author/desc/keyword in problematic encoding
2105 2111 # unicode: cp932:
2106 2112 # u30A2 0x83 0x41(= 'A')
2107 2113 # u30C2 0x83 0x61(= 'a')
2108 2114
2109 2115 $ hg init problematicencoding
2110 2116 $ cd problematicencoding
2111 2117
2112 2118 $ python > setup.sh <<EOF
2113 2119 > print u'''
2114 2120 > echo a > text
2115 2121 > hg add text
2116 2122 > hg --encoding utf-8 commit -u '\u30A2' -m none
2117 2123 > echo b > text
2118 2124 > hg --encoding utf-8 commit -u '\u30C2' -m none
2119 2125 > echo c > text
2120 2126 > hg --encoding utf-8 commit -u none -m '\u30A2'
2121 2127 > echo d > text
2122 2128 > hg --encoding utf-8 commit -u none -m '\u30C2'
2123 2129 > '''.encode('utf-8')
2124 2130 > EOF
2125 2131 $ sh < setup.sh
2126 2132
2127 2133 test in problematic encoding
2128 2134 $ python > test.sh <<EOF
2129 2135 > print u'''
2130 2136 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30A2)'
2131 2137 > echo ====
2132 2138 > hg --encoding cp932 log --template '{rev}\\n' -r 'author(\u30C2)'
2133 2139 > echo ====
2134 2140 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30A2)'
2135 2141 > echo ====
2136 2142 > hg --encoding cp932 log --template '{rev}\\n' -r 'desc(\u30C2)'
2137 2143 > echo ====
2138 2144 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30A2)'
2139 2145 > echo ====
2140 2146 > hg --encoding cp932 log --template '{rev}\\n' -r 'keyword(\u30C2)'
2141 2147 > '''.encode('cp932')
2142 2148 > EOF
2143 2149 $ sh < test.sh
2144 2150 0
2145 2151 ====
2146 2152 1
2147 2153 ====
2148 2154 2
2149 2155 ====
2150 2156 3
2151 2157 ====
2152 2158 0
2153 2159 2
2154 2160 ====
2155 2161 1
2156 2162 3
2157 2163
2158 2164 test error message of bad revset
2159 2165 $ hg log -r 'foo\\'
2160 2166 hg: parse error at 3: syntax error in revset 'foo\\'
2161 2167 [255]
2162 2168
2163 2169 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now