##// END OF EJS Templates
revset: make follow() accept multiple startrevs...
Yuya Nishihara -
r35299:921680c3 default
parent child Browse files
Show More
@@ -1,2221 +1,2221
1 1 # revset.py - revision set queries for mercurial
2 2 #
3 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import re
11 11
12 12 from .i18n import _
13 13 from . import (
14 14 dagop,
15 15 destutil,
16 16 encoding,
17 17 error,
18 18 hbisect,
19 19 match as matchmod,
20 20 node,
21 21 obsolete as obsmod,
22 22 obsutil,
23 23 pathutil,
24 24 phases,
25 25 registrar,
26 26 repoview,
27 27 revsetlang,
28 28 scmutil,
29 29 smartset,
30 30 util,
31 31 )
32 32
33 33 # helpers for processing parsed tree
34 34 getsymbol = revsetlang.getsymbol
35 35 getstring = revsetlang.getstring
36 36 getinteger = revsetlang.getinteger
37 37 getboolean = revsetlang.getboolean
38 38 getlist = revsetlang.getlist
39 39 getrange = revsetlang.getrange
40 40 getargs = revsetlang.getargs
41 41 getargsdict = revsetlang.getargsdict
42 42
43 43 baseset = smartset.baseset
44 44 generatorset = smartset.generatorset
45 45 spanset = smartset.spanset
46 46 fullreposet = smartset.fullreposet
47 47
48 48 # Constants for ordering requirement, used in getset():
49 49 #
50 50 # If 'define', any nested functions and operations MAY change the ordering of
51 51 # the entries in the set (but if changes the ordering, it MUST ALWAYS change
52 52 # it). If 'follow', any nested functions and operations MUST take the ordering
53 53 # specified by the first operand to the '&' operator.
54 54 #
55 55 # For instance,
56 56 #
57 57 # X & (Y | Z)
58 58 # ^ ^^^^^^^
59 59 # | follow
60 60 # define
61 61 #
62 62 # will be evaluated as 'or(y(x()), z(x()))', where 'x()' can change the order
63 63 # of the entries in the set, but 'y()', 'z()' and 'or()' shouldn't.
64 64 #
65 65 # 'any' means the order doesn't matter. For instance,
66 66 #
67 67 # (X & !Y) | ancestors(Z)
68 68 # ^ ^
69 69 # any any
70 70 #
71 71 # For 'X & !Y', 'X' decides the order and 'Y' is subtracted from 'X', so the
72 72 # order of 'Y' does not matter. For 'ancestors(Z)', Z's order does not matter
73 73 # since 'ancestors' does not care about the order of its argument.
74 74 #
75 75 # Currently, most revsets do not care about the order, so 'define' is
76 76 # equivalent to 'follow' for them, and the resulting order is based on the
77 77 # 'subset' parameter passed down to them:
78 78 #
79 79 # m = revset.match(...)
80 80 # m(repo, subset, order=defineorder)
81 81 # ^^^^^^
82 82 # For most revsets, 'define' means using the order this subset provides
83 83 #
84 84 # There are a few revsets that always redefine the order if 'define' is
85 85 # specified: 'sort(X)', 'reverse(X)', 'x:y'.
86 86 anyorder = 'any' # don't care the order, could be even random-shuffled
87 87 defineorder = 'define' # ALWAYS redefine, or ALWAYS follow the current order
88 88 followorder = 'follow' # MUST follow the current order
89 89
90 90 # helpers
91 91
92 92 def getset(repo, subset, x, order=defineorder):
93 93 if not x:
94 94 raise error.ParseError(_("missing argument"))
95 95 return methods[x[0]](repo, subset, *x[1:], order=order)
96 96
97 97 def _getrevsource(repo, r):
98 98 extra = repo[r].extra()
99 99 for label in ('source', 'transplant_source', 'rebase_source'):
100 100 if label in extra:
101 101 try:
102 102 return repo[extra[label]].rev()
103 103 except error.RepoLookupError:
104 104 pass
105 105 return None
106 106
107 107 # operator methods
108 108
109 109 def stringset(repo, subset, x, order):
110 110 x = scmutil.intrev(repo[x])
111 111 if (x in subset
112 112 or x == node.nullrev and isinstance(subset, fullreposet)):
113 113 return baseset([x])
114 114 return baseset()
115 115
116 116 def rangeset(repo, subset, x, y, order):
117 117 m = getset(repo, fullreposet(repo), x)
118 118 n = getset(repo, fullreposet(repo), y)
119 119
120 120 if not m or not n:
121 121 return baseset()
122 122 return _makerangeset(repo, subset, m.first(), n.last(), order)
123 123
124 124 def rangeall(repo, subset, x, order):
125 125 assert x is None
126 126 return _makerangeset(repo, subset, 0, len(repo) - 1, order)
127 127
128 128 def rangepre(repo, subset, y, order):
129 129 # ':y' can't be rewritten to '0:y' since '0' may be hidden
130 130 n = getset(repo, fullreposet(repo), y)
131 131 if not n:
132 132 return baseset()
133 133 return _makerangeset(repo, subset, 0, n.last(), order)
134 134
135 135 def rangepost(repo, subset, x, order):
136 136 m = getset(repo, fullreposet(repo), x)
137 137 if not m:
138 138 return baseset()
139 139 return _makerangeset(repo, subset, m.first(), len(repo) - 1, order)
140 140
141 141 def _makerangeset(repo, subset, m, n, order):
142 142 if m == n:
143 143 r = baseset([m])
144 144 elif n == node.wdirrev:
145 145 r = spanset(repo, m, len(repo)) + baseset([n])
146 146 elif m == node.wdirrev:
147 147 r = baseset([m]) + spanset(repo, len(repo) - 1, n - 1)
148 148 elif m < n:
149 149 r = spanset(repo, m, n + 1)
150 150 else:
151 151 r = spanset(repo, m, n - 1)
152 152
153 153 if order == defineorder:
154 154 return r & subset
155 155 else:
156 156 # carrying the sorting over when possible would be more efficient
157 157 return subset & r
158 158
159 159 def dagrange(repo, subset, x, y, order):
160 160 r = fullreposet(repo)
161 161 xs = dagop.reachableroots(repo, getset(repo, r, x), getset(repo, r, y),
162 162 includepath=True)
163 163 return subset & xs
164 164
165 165 def andset(repo, subset, x, y, order):
166 166 if order == anyorder:
167 167 yorder = anyorder
168 168 else:
169 169 yorder = followorder
170 170 return getset(repo, getset(repo, subset, x, order), y, yorder)
171 171
172 172 def andsmallyset(repo, subset, x, y, order):
173 173 # 'andsmally(x, y)' is equivalent to 'and(x, y)', but faster when y is small
174 174 if order == anyorder:
175 175 yorder = anyorder
176 176 else:
177 177 yorder = followorder
178 178 return getset(repo, getset(repo, subset, y, yorder), x, order)
179 179
180 180 def differenceset(repo, subset, x, y, order):
181 181 return getset(repo, subset, x, order) - getset(repo, subset, y, anyorder)
182 182
183 183 def _orsetlist(repo, subset, xs, order):
184 184 assert xs
185 185 if len(xs) == 1:
186 186 return getset(repo, subset, xs[0], order)
187 187 p = len(xs) // 2
188 188 a = _orsetlist(repo, subset, xs[:p], order)
189 189 b = _orsetlist(repo, subset, xs[p:], order)
190 190 return a + b
191 191
192 192 def orset(repo, subset, x, order):
193 193 xs = getlist(x)
194 194 if order == followorder:
195 195 # slow path to take the subset order
196 196 return subset & _orsetlist(repo, fullreposet(repo), xs, anyorder)
197 197 else:
198 198 return _orsetlist(repo, subset, xs, order)
199 199
200 200 def notset(repo, subset, x, order):
201 201 return subset - getset(repo, subset, x, anyorder)
202 202
203 203 def relationset(repo, subset, x, y, order):
204 204 raise error.ParseError(_("can't use a relation in this context"))
205 205
206 206 def relsubscriptset(repo, subset, x, y, z, order):
207 207 # this is pretty basic implementation of 'x#y[z]' operator, still
208 208 # experimental so undocumented. see the wiki for further ideas.
209 209 # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan
210 210 rel = getsymbol(y)
211 211 n = getinteger(z, _("relation subscript must be an integer"))
212 212
213 213 # TODO: perhaps this should be a table of relation functions
214 214 if rel in ('g', 'generations'):
215 215 # TODO: support range, rewrite tests, and drop startdepth argument
216 216 # from ancestors() and descendants() predicates
217 217 if n <= 0:
218 218 n = -n
219 219 return _ancestors(repo, subset, x, startdepth=n, stopdepth=n + 1)
220 220 else:
221 221 return _descendants(repo, subset, x, startdepth=n, stopdepth=n + 1)
222 222
223 223 raise error.UnknownIdentifier(rel, ['generations'])
224 224
225 225 def subscriptset(repo, subset, x, y, order):
226 226 raise error.ParseError(_("can't use a subscript in this context"))
227 227
228 228 def listset(repo, subset, *xs, **opts):
229 229 raise error.ParseError(_("can't use a list in this context"),
230 230 hint=_('see hg help "revsets.x or y"'))
231 231
232 232 def keyvaluepair(repo, subset, k, v, order):
233 233 raise error.ParseError(_("can't use a key-value pair in this context"))
234 234
235 235 def func(repo, subset, a, b, order):
236 236 f = getsymbol(a)
237 237 if f in symbols:
238 238 func = symbols[f]
239 239 if getattr(func, '_takeorder', False):
240 240 return func(repo, subset, b, order)
241 241 return func(repo, subset, b)
242 242
243 243 keep = lambda fn: getattr(fn, '__doc__', None) is not None
244 244
245 245 syms = [s for (s, fn) in symbols.items() if keep(fn)]
246 246 raise error.UnknownIdentifier(f, syms)
247 247
248 248 # functions
249 249
250 250 # symbols are callables like:
251 251 # fn(repo, subset, x)
252 252 # with:
253 253 # repo - current repository instance
254 254 # subset - of revisions to be examined
255 255 # x - argument in tree form
256 256 symbols = revsetlang.symbols
257 257
258 258 # symbols which can't be used for a DoS attack for any given input
259 259 # (e.g. those which accept regexes as plain strings shouldn't be included)
260 260 # functions that just return a lot of changesets (like all) don't count here
261 261 safesymbols = set()
262 262
263 263 predicate = registrar.revsetpredicate()
264 264
265 265 @predicate('_destupdate')
266 266 def _destupdate(repo, subset, x):
267 267 # experimental revset for update destination
268 268 args = getargsdict(x, 'limit', 'clean')
269 269 return subset & baseset([destutil.destupdate(repo, **args)[0]])
270 270
271 271 @predicate('_destmerge')
272 272 def _destmerge(repo, subset, x):
273 273 # experimental revset for merge destination
274 274 sourceset = None
275 275 if x is not None:
276 276 sourceset = getset(repo, fullreposet(repo), x)
277 277 return subset & baseset([destutil.destmerge(repo, sourceset=sourceset)])
278 278
279 279 @predicate('adds(pattern)', safe=True, weight=30)
280 280 def adds(repo, subset, x):
281 281 """Changesets that add a file matching pattern.
282 282
283 283 The pattern without explicit kind like ``glob:`` is expected to be
284 284 relative to the current directory and match against a file or a
285 285 directory.
286 286 """
287 287 # i18n: "adds" is a keyword
288 288 pat = getstring(x, _("adds requires a pattern"))
289 289 return checkstatus(repo, subset, pat, 1)
290 290
291 291 @predicate('ancestor(*changeset)', safe=True, weight=0.5)
292 292 def ancestor(repo, subset, x):
293 293 """A greatest common ancestor of the changesets.
294 294
295 295 Accepts 0 or more changesets.
296 296 Will return empty list when passed no args.
297 297 Greatest common ancestor of a single changeset is that changeset.
298 298 """
299 299 # i18n: "ancestor" is a keyword
300 300 l = getlist(x)
301 301 rl = fullreposet(repo)
302 302 anc = None
303 303
304 304 # (getset(repo, rl, i) for i in l) generates a list of lists
305 305 for revs in (getset(repo, rl, i) for i in l):
306 306 for r in revs:
307 307 if anc is None:
308 308 anc = repo[r]
309 309 else:
310 310 anc = anc.ancestor(repo[r])
311 311
312 312 if anc is not None and anc.rev() in subset:
313 313 return baseset([anc.rev()])
314 314 return baseset()
315 315
316 316 def _ancestors(repo, subset, x, followfirst=False, startdepth=None,
317 317 stopdepth=None):
318 318 heads = getset(repo, fullreposet(repo), x)
319 319 if not heads:
320 320 return baseset()
321 321 s = dagop.revancestors(repo, heads, followfirst, startdepth, stopdepth)
322 322 return subset & s
323 323
324 324 @predicate('ancestors(set[, depth])', safe=True)
325 325 def ancestors(repo, subset, x):
326 326 """Changesets that are ancestors of changesets in set, including the
327 327 given changesets themselves.
328 328
329 329 If depth is specified, the result only includes changesets up to
330 330 the specified generation.
331 331 """
332 332 # startdepth is for internal use only until we can decide the UI
333 333 args = getargsdict(x, 'ancestors', 'set depth startdepth')
334 334 if 'set' not in args:
335 335 # i18n: "ancestors" is a keyword
336 336 raise error.ParseError(_('ancestors takes at least 1 argument'))
337 337 startdepth = stopdepth = None
338 338 if 'startdepth' in args:
339 339 n = getinteger(args['startdepth'],
340 340 "ancestors expects an integer startdepth")
341 341 if n < 0:
342 342 raise error.ParseError("negative startdepth")
343 343 startdepth = n
344 344 if 'depth' in args:
345 345 # i18n: "ancestors" is a keyword
346 346 n = getinteger(args['depth'], _("ancestors expects an integer depth"))
347 347 if n < 0:
348 348 raise error.ParseError(_("negative depth"))
349 349 stopdepth = n + 1
350 350 return _ancestors(repo, subset, args['set'],
351 351 startdepth=startdepth, stopdepth=stopdepth)
352 352
353 353 @predicate('_firstancestors', safe=True)
354 354 def _firstancestors(repo, subset, x):
355 355 # ``_firstancestors(set)``
356 356 # Like ``ancestors(set)`` but follows only the first parents.
357 357 return _ancestors(repo, subset, x, followfirst=True)
358 358
359 359 def _childrenspec(repo, subset, x, n, order):
360 360 """Changesets that are the Nth child of a changeset
361 361 in set.
362 362 """
363 363 cs = set()
364 364 for r in getset(repo, fullreposet(repo), x):
365 365 for i in range(n):
366 366 c = repo[r].children()
367 367 if len(c) == 0:
368 368 break
369 369 if len(c) > 1:
370 370 raise error.RepoLookupError(
371 371 _("revision in set has more than one child"))
372 372 r = c[0].rev()
373 373 else:
374 374 cs.add(r)
375 375 return subset & cs
376 376
377 377 def ancestorspec(repo, subset, x, n, order):
378 378 """``set~n``
379 379 Changesets that are the Nth ancestor (first parents only) of a changeset
380 380 in set.
381 381 """
382 382 n = getinteger(n, _("~ expects a number"))
383 383 if n < 0:
384 384 # children lookup
385 385 return _childrenspec(repo, subset, x, -n, order)
386 386 ps = set()
387 387 cl = repo.changelog
388 388 for r in getset(repo, fullreposet(repo), x):
389 389 for i in range(n):
390 390 try:
391 391 r = cl.parentrevs(r)[0]
392 392 except error.WdirUnsupported:
393 393 r = repo[r].parents()[0].rev()
394 394 ps.add(r)
395 395 return subset & ps
396 396
397 397 @predicate('author(string)', safe=True, weight=10)
398 398 def author(repo, subset, x):
399 399 """Alias for ``user(string)``.
400 400 """
401 401 # i18n: "author" is a keyword
402 402 n = getstring(x, _("author requires a string"))
403 403 kind, pattern, matcher = _substringmatcher(n, casesensitive=False)
404 404 return subset.filter(lambda x: matcher(repo[x].user()),
405 405 condrepr=('<user %r>', n))
406 406
407 407 @predicate('bisect(string)', safe=True)
408 408 def bisect(repo, subset, x):
409 409 """Changesets marked in the specified bisect status:
410 410
411 411 - ``good``, ``bad``, ``skip``: csets explicitly marked as good/bad/skip
412 412 - ``goods``, ``bads`` : csets topologically good/bad
413 413 - ``range`` : csets taking part in the bisection
414 414 - ``pruned`` : csets that are goods, bads or skipped
415 415 - ``untested`` : csets whose fate is yet unknown
416 416 - ``ignored`` : csets ignored due to DAG topology
417 417 - ``current`` : the cset currently being bisected
418 418 """
419 419 # i18n: "bisect" is a keyword
420 420 status = getstring(x, _("bisect requires a string")).lower()
421 421 state = set(hbisect.get(repo, status))
422 422 return subset & state
423 423
424 424 # Backward-compatibility
425 425 # - no help entry so that we do not advertise it any more
426 426 @predicate('bisected', safe=True)
427 427 def bisected(repo, subset, x):
428 428 return bisect(repo, subset, x)
429 429
430 430 @predicate('bookmark([name])', safe=True)
431 431 def bookmark(repo, subset, x):
432 432 """The named bookmark or all bookmarks.
433 433
434 434 Pattern matching is supported for `name`. See :hg:`help revisions.patterns`.
435 435 """
436 436 # i18n: "bookmark" is a keyword
437 437 args = getargs(x, 0, 1, _('bookmark takes one or no arguments'))
438 438 if args:
439 439 bm = getstring(args[0],
440 440 # i18n: "bookmark" is a keyword
441 441 _('the argument to bookmark must be a string'))
442 442 kind, pattern, matcher = util.stringmatcher(bm)
443 443 bms = set()
444 444 if kind == 'literal':
445 445 bmrev = repo._bookmarks.get(pattern, None)
446 446 if not bmrev:
447 447 raise error.RepoLookupError(_("bookmark '%s' does not exist")
448 448 % pattern)
449 449 bms.add(repo[bmrev].rev())
450 450 else:
451 451 matchrevs = set()
452 452 for name, bmrev in repo._bookmarks.iteritems():
453 453 if matcher(name):
454 454 matchrevs.add(bmrev)
455 455 if not matchrevs:
456 456 raise error.RepoLookupError(_("no bookmarks exist"
457 457 " that match '%s'") % pattern)
458 458 for bmrev in matchrevs:
459 459 bms.add(repo[bmrev].rev())
460 460 else:
461 461 bms = {repo[r].rev() for r in repo._bookmarks.values()}
462 462 bms -= {node.nullrev}
463 463 return subset & bms
464 464
465 465 @predicate('branch(string or set)', safe=True, weight=10)
466 466 def branch(repo, subset, x):
467 467 """
468 468 All changesets belonging to the given branch or the branches of the given
469 469 changesets.
470 470
471 471 Pattern matching is supported for `string`. See
472 472 :hg:`help revisions.patterns`.
473 473 """
474 474 getbi = repo.revbranchcache().branchinfo
475 475 def getbranch(r):
476 476 try:
477 477 return getbi(r)[0]
478 478 except error.WdirUnsupported:
479 479 return repo[r].branch()
480 480
481 481 try:
482 482 b = getstring(x, '')
483 483 except error.ParseError:
484 484 # not a string, but another revspec, e.g. tip()
485 485 pass
486 486 else:
487 487 kind, pattern, matcher = util.stringmatcher(b)
488 488 if kind == 'literal':
489 489 # note: falls through to the revspec case if no branch with
490 490 # this name exists and pattern kind is not specified explicitly
491 491 if pattern in repo.branchmap():
492 492 return subset.filter(lambda r: matcher(getbranch(r)),
493 493 condrepr=('<branch %r>', b))
494 494 if b.startswith('literal:'):
495 495 raise error.RepoLookupError(_("branch '%s' does not exist")
496 496 % pattern)
497 497 else:
498 498 return subset.filter(lambda r: matcher(getbranch(r)),
499 499 condrepr=('<branch %r>', b))
500 500
501 501 s = getset(repo, fullreposet(repo), x)
502 502 b = set()
503 503 for r in s:
504 504 b.add(getbranch(r))
505 505 c = s.__contains__
506 506 return subset.filter(lambda r: c(r) or getbranch(r) in b,
507 507 condrepr=lambda: '<branch %r>' % sorted(b))
508 508
509 509 @predicate('bumped()', safe=True)
510 510 def bumped(repo, subset, x):
511 511 msg = ("'bumped()' is deprecated, "
512 512 "use 'phasedivergent()'")
513 513 repo.ui.deprecwarn(msg, '4.4')
514 514
515 515 return phasedivergent(repo, subset, x)
516 516
517 517 @predicate('phasedivergent()', safe=True)
518 518 def phasedivergent(repo, subset, x):
519 519 """Mutable changesets marked as successors of public changesets.
520 520
521 521 Only non-public and non-obsolete changesets can be `phasedivergent`.
522 522 (EXPERIMENTAL)
523 523 """
524 524 # i18n: "phasedivergent" is a keyword
525 525 getargs(x, 0, 0, _("phasedivergent takes no arguments"))
526 526 phasedivergent = obsmod.getrevs(repo, 'phasedivergent')
527 527 return subset & phasedivergent
528 528
529 529 @predicate('bundle()', safe=True)
530 530 def bundle(repo, subset, x):
531 531 """Changesets in the bundle.
532 532
533 533 Bundle must be specified by the -R option."""
534 534
535 535 try:
536 536 bundlerevs = repo.changelog.bundlerevs
537 537 except AttributeError:
538 538 raise error.Abort(_("no bundle provided - specify with -R"))
539 539 return subset & bundlerevs
540 540
541 541 def checkstatus(repo, subset, pat, field):
542 542 hasset = matchmod.patkind(pat) == 'set'
543 543
544 544 mcache = [None]
545 545 def matches(x):
546 546 c = repo[x]
547 547 if not mcache[0] or hasset:
548 548 mcache[0] = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
549 549 m = mcache[0]
550 550 fname = None
551 551 if not m.anypats() and len(m.files()) == 1:
552 552 fname = m.files()[0]
553 553 if fname is not None:
554 554 if fname not in c.files():
555 555 return False
556 556 else:
557 557 for f in c.files():
558 558 if m(f):
559 559 break
560 560 else:
561 561 return False
562 562 files = repo.status(c.p1().node(), c.node())[field]
563 563 if fname is not None:
564 564 if fname in files:
565 565 return True
566 566 else:
567 567 for f in files:
568 568 if m(f):
569 569 return True
570 570
571 571 return subset.filter(matches, condrepr=('<status[%r] %r>', field, pat))
572 572
573 573 def _children(repo, subset, parentset):
574 574 if not parentset:
575 575 return baseset()
576 576 cs = set()
577 577 pr = repo.changelog.parentrevs
578 578 minrev = parentset.min()
579 579 nullrev = node.nullrev
580 580 for r in subset:
581 581 if r <= minrev:
582 582 continue
583 583 p1, p2 = pr(r)
584 584 if p1 in parentset:
585 585 cs.add(r)
586 586 if p2 != nullrev and p2 in parentset:
587 587 cs.add(r)
588 588 return baseset(cs)
589 589
590 590 @predicate('children(set)', safe=True)
591 591 def children(repo, subset, x):
592 592 """Child changesets of changesets in set.
593 593 """
594 594 s = getset(repo, fullreposet(repo), x)
595 595 cs = _children(repo, subset, s)
596 596 return subset & cs
597 597
598 598 @predicate('closed()', safe=True, weight=10)
599 599 def closed(repo, subset, x):
600 600 """Changeset is closed.
601 601 """
602 602 # i18n: "closed" is a keyword
603 603 getargs(x, 0, 0, _("closed takes no arguments"))
604 604 return subset.filter(lambda r: repo[r].closesbranch(),
605 605 condrepr='<branch closed>')
606 606
607 607 @predicate('contains(pattern)', weight=100)
608 608 def contains(repo, subset, x):
609 609 """The revision's manifest contains a file matching pattern (but might not
610 610 modify it). See :hg:`help patterns` for information about file patterns.
611 611
612 612 The pattern without explicit kind like ``glob:`` is expected to be
613 613 relative to the current directory and match against a file exactly
614 614 for efficiency.
615 615 """
616 616 # i18n: "contains" is a keyword
617 617 pat = getstring(x, _("contains requires a pattern"))
618 618
619 619 def matches(x):
620 620 if not matchmod.patkind(pat):
621 621 pats = pathutil.canonpath(repo.root, repo.getcwd(), pat)
622 622 if pats in repo[x]:
623 623 return True
624 624 else:
625 625 c = repo[x]
626 626 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=c)
627 627 for f in c.manifest():
628 628 if m(f):
629 629 return True
630 630 return False
631 631
632 632 return subset.filter(matches, condrepr=('<contains %r>', pat))
633 633
634 634 @predicate('converted([id])', safe=True)
635 635 def converted(repo, subset, x):
636 636 """Changesets converted from the given identifier in the old repository if
637 637 present, or all converted changesets if no identifier is specified.
638 638 """
639 639
640 640 # There is exactly no chance of resolving the revision, so do a simple
641 641 # string compare and hope for the best
642 642
643 643 rev = None
644 644 # i18n: "converted" is a keyword
645 645 l = getargs(x, 0, 1, _('converted takes one or no arguments'))
646 646 if l:
647 647 # i18n: "converted" is a keyword
648 648 rev = getstring(l[0], _('converted requires a revision'))
649 649
650 650 def _matchvalue(r):
651 651 source = repo[r].extra().get('convert_revision', None)
652 652 return source is not None and (rev is None or source.startswith(rev))
653 653
654 654 return subset.filter(lambda r: _matchvalue(r),
655 655 condrepr=('<converted %r>', rev))
656 656
657 657 @predicate('date(interval)', safe=True, weight=10)
658 658 def date(repo, subset, x):
659 659 """Changesets within the interval, see :hg:`help dates`.
660 660 """
661 661 # i18n: "date" is a keyword
662 662 ds = getstring(x, _("date requires a string"))
663 663 dm = util.matchdate(ds)
664 664 return subset.filter(lambda x: dm(repo[x].date()[0]),
665 665 condrepr=('<date %r>', ds))
666 666
667 667 @predicate('desc(string)', safe=True, weight=10)
668 668 def desc(repo, subset, x):
669 669 """Search commit message for string. The match is case-insensitive.
670 670
671 671 Pattern matching is supported for `string`. See
672 672 :hg:`help revisions.patterns`.
673 673 """
674 674 # i18n: "desc" is a keyword
675 675 ds = getstring(x, _("desc requires a string"))
676 676
677 677 kind, pattern, matcher = _substringmatcher(ds, casesensitive=False)
678 678
679 679 return subset.filter(lambda r: matcher(repo[r].description()),
680 680 condrepr=('<desc %r>', ds))
681 681
682 682 def _descendants(repo, subset, x, followfirst=False, startdepth=None,
683 683 stopdepth=None):
684 684 roots = getset(repo, fullreposet(repo), x)
685 685 if not roots:
686 686 return baseset()
687 687 s = dagop.revdescendants(repo, roots, followfirst, startdepth, stopdepth)
688 688 return subset & s
689 689
690 690 @predicate('descendants(set[, depth])', safe=True)
691 691 def descendants(repo, subset, x):
692 692 """Changesets which are descendants of changesets in set, including the
693 693 given changesets themselves.
694 694
695 695 If depth is specified, the result only includes changesets up to
696 696 the specified generation.
697 697 """
698 698 # startdepth is for internal use only until we can decide the UI
699 699 args = getargsdict(x, 'descendants', 'set depth startdepth')
700 700 if 'set' not in args:
701 701 # i18n: "descendants" is a keyword
702 702 raise error.ParseError(_('descendants takes at least 1 argument'))
703 703 startdepth = stopdepth = None
704 704 if 'startdepth' in args:
705 705 n = getinteger(args['startdepth'],
706 706 "descendants expects an integer startdepth")
707 707 if n < 0:
708 708 raise error.ParseError("negative startdepth")
709 709 startdepth = n
710 710 if 'depth' in args:
711 711 # i18n: "descendants" is a keyword
712 712 n = getinteger(args['depth'], _("descendants expects an integer depth"))
713 713 if n < 0:
714 714 raise error.ParseError(_("negative depth"))
715 715 stopdepth = n + 1
716 716 return _descendants(repo, subset, args['set'],
717 717 startdepth=startdepth, stopdepth=stopdepth)
718 718
719 719 @predicate('_firstdescendants', safe=True)
720 720 def _firstdescendants(repo, subset, x):
721 721 # ``_firstdescendants(set)``
722 722 # Like ``descendants(set)`` but follows only the first parents.
723 723 return _descendants(repo, subset, x, followfirst=True)
724 724
725 725 @predicate('destination([set])', safe=True, weight=10)
726 726 def destination(repo, subset, x):
727 727 """Changesets that were created by a graft, transplant or rebase operation,
728 728 with the given revisions specified as the source. Omitting the optional set
729 729 is the same as passing all().
730 730 """
731 731 if x is not None:
732 732 sources = getset(repo, fullreposet(repo), x)
733 733 else:
734 734 sources = fullreposet(repo)
735 735
736 736 dests = set()
737 737
738 738 # subset contains all of the possible destinations that can be returned, so
739 739 # iterate over them and see if their source(s) were provided in the arg set.
740 740 # Even if the immediate src of r is not in the arg set, src's source (or
741 741 # further back) may be. Scanning back further than the immediate src allows
742 742 # transitive transplants and rebases to yield the same results as transitive
743 743 # grafts.
744 744 for r in subset:
745 745 src = _getrevsource(repo, r)
746 746 lineage = None
747 747
748 748 while src is not None:
749 749 if lineage is None:
750 750 lineage = list()
751 751
752 752 lineage.append(r)
753 753
754 754 # The visited lineage is a match if the current source is in the arg
755 755 # set. Since every candidate dest is visited by way of iterating
756 756 # subset, any dests further back in the lineage will be tested by a
757 757 # different iteration over subset. Likewise, if the src was already
758 758 # selected, the current lineage can be selected without going back
759 759 # further.
760 760 if src in sources or src in dests:
761 761 dests.update(lineage)
762 762 break
763 763
764 764 r = src
765 765 src = _getrevsource(repo, r)
766 766
767 767 return subset.filter(dests.__contains__,
768 768 condrepr=lambda: '<destination %r>' % sorted(dests))
769 769
770 770 @predicate('divergent()', safe=True)
771 771 def divergent(repo, subset, x):
772 772 msg = ("'divergent()' is deprecated, "
773 773 "use 'contentdivergent()'")
774 774 repo.ui.deprecwarn(msg, '4.4')
775 775
776 776 return contentdivergent(repo, subset, x)
777 777
778 778 @predicate('contentdivergent()', safe=True)
779 779 def contentdivergent(repo, subset, x):
780 780 """
781 781 Final successors of changesets with an alternative set of final
782 782 successors. (EXPERIMENTAL)
783 783 """
784 784 # i18n: "contentdivergent" is a keyword
785 785 getargs(x, 0, 0, _("contentdivergent takes no arguments"))
786 786 contentdivergent = obsmod.getrevs(repo, 'contentdivergent')
787 787 return subset & contentdivergent
788 788
789 789 @predicate('extdata(source)', safe=False, weight=100)
790 790 def extdata(repo, subset, x):
791 791 """Changesets in the specified extdata source. (EXPERIMENTAL)"""
792 792 # i18n: "extdata" is a keyword
793 793 args = getargsdict(x, 'extdata', 'source')
794 794 source = getstring(args.get('source'),
795 795 # i18n: "extdata" is a keyword
796 796 _('extdata takes at least 1 string argument'))
797 797 data = scmutil.extdatasource(repo, source)
798 798 return subset & baseset(data)
799 799
800 800 @predicate('extinct()', safe=True)
801 801 def extinct(repo, subset, x):
802 802 """Obsolete changesets with obsolete descendants only.
803 803 """
804 804 # i18n: "extinct" is a keyword
805 805 getargs(x, 0, 0, _("extinct takes no arguments"))
806 806 extincts = obsmod.getrevs(repo, 'extinct')
807 807 return subset & extincts
808 808
809 809 @predicate('extra(label, [value])', safe=True)
810 810 def extra(repo, subset, x):
811 811 """Changesets with the given label in the extra metadata, with the given
812 812 optional value.
813 813
814 814 Pattern matching is supported for `value`. See
815 815 :hg:`help revisions.patterns`.
816 816 """
817 817 args = getargsdict(x, 'extra', 'label value')
818 818 if 'label' not in args:
819 819 # i18n: "extra" is a keyword
820 820 raise error.ParseError(_('extra takes at least 1 argument'))
821 821 # i18n: "extra" is a keyword
822 822 label = getstring(args['label'], _('first argument to extra must be '
823 823 'a string'))
824 824 value = None
825 825
826 826 if 'value' in args:
827 827 # i18n: "extra" is a keyword
828 828 value = getstring(args['value'], _('second argument to extra must be '
829 829 'a string'))
830 830 kind, value, matcher = util.stringmatcher(value)
831 831
832 832 def _matchvalue(r):
833 833 extra = repo[r].extra()
834 834 return label in extra and (value is None or matcher(extra[label]))
835 835
836 836 return subset.filter(lambda r: _matchvalue(r),
837 837 condrepr=('<extra[%r] %r>', label, value))
838 838
839 839 @predicate('filelog(pattern)', safe=True)
840 840 def filelog(repo, subset, x):
841 841 """Changesets connected to the specified filelog.
842 842
843 843 For performance reasons, visits only revisions mentioned in the file-level
844 844 filelog, rather than filtering through all changesets (much faster, but
845 845 doesn't include deletes or duplicate changes). For a slower, more accurate
846 846 result, use ``file()``.
847 847
848 848 The pattern without explicit kind like ``glob:`` is expected to be
849 849 relative to the current directory and match against a file exactly
850 850 for efficiency.
851 851
852 852 If some linkrev points to revisions filtered by the current repoview, we'll
853 853 work around it to return a non-filtered value.
854 854 """
855 855
856 856 # i18n: "filelog" is a keyword
857 857 pat = getstring(x, _("filelog requires a pattern"))
858 858 s = set()
859 859 cl = repo.changelog
860 860
861 861 if not matchmod.patkind(pat):
862 862 f = pathutil.canonpath(repo.root, repo.getcwd(), pat)
863 863 files = [f]
864 864 else:
865 865 m = matchmod.match(repo.root, repo.getcwd(), [pat], ctx=repo[None])
866 866 files = (f for f in repo[None] if m(f))
867 867
868 868 for f in files:
869 869 fl = repo.file(f)
870 870 known = {}
871 871 scanpos = 0
872 872 for fr in list(fl):
873 873 fn = fl.node(fr)
874 874 if fn in known:
875 875 s.add(known[fn])
876 876 continue
877 877
878 878 lr = fl.linkrev(fr)
879 879 if lr in cl:
880 880 s.add(lr)
881 881 elif scanpos is not None:
882 882 # lowest matching changeset is filtered, scan further
883 883 # ahead in changelog
884 884 start = max(lr, scanpos) + 1
885 885 scanpos = None
886 886 for r in cl.revs(start):
887 887 # minimize parsing of non-matching entries
888 888 if f in cl.revision(r) and f in cl.readfiles(r):
889 889 try:
890 890 # try to use manifest delta fastpath
891 891 n = repo[r].filenode(f)
892 892 if n not in known:
893 893 if n == fn:
894 894 s.add(r)
895 895 scanpos = r
896 896 break
897 897 else:
898 898 known[n] = r
899 899 except error.ManifestLookupError:
900 900 # deletion in changelog
901 901 continue
902 902
903 903 return subset & s
904 904
905 905 @predicate('first(set, [n])', safe=True, takeorder=True, weight=0)
906 906 def first(repo, subset, x, order):
907 907 """An alias for limit().
908 908 """
909 909 return limit(repo, subset, x, order)
910 910
911 911 def _follow(repo, subset, x, name, followfirst=False):
912 912 l = getargs(x, 0, 2, _("%s takes no arguments or a pattern "
913 913 "and an optional revset") % name)
914 914 c = repo['.']
915 915 if l:
916 916 x = getstring(l[0], _("%s expected a pattern") % name)
917 rev = None
917 revs = [None]
918 918 if len(l) >= 2:
919 919 revs = getset(repo, fullreposet(repo), l[1])
920 if len(revs) != 1:
920 if not revs:
921 921 raise error.RepoLookupError(
922 _("%s expected one starting revision") % name)
923 rev = revs.last()
924 c = repo[rev]
925 matcher = matchmod.match(repo.root, repo.getcwd(), [x],
926 ctx=repo[rev], default='path')
927
928 files = c.manifest().walk(matcher)
929
930 fctxs = [c[f].introfilectx() for f in files]
922 _("%s expected at least one starting revision") % name)
923 fctxs = []
924 for r in revs:
925 ctx = mctx = repo[r]
926 if r is None:
927 ctx = repo['.']
928 m = matchmod.match(repo.root, repo.getcwd(), [x],
929 ctx=mctx, default='path')
930 fctxs.extend(ctx[f].introfilectx() for f in ctx.manifest().walk(m))
931 931 s = dagop.filerevancestors(fctxs, followfirst)
932 932 else:
933 933 s = dagop.revancestors(repo, baseset([c.rev()]), followfirst)
934 934
935 935 return subset & s
936 936
937 937 @predicate('follow([pattern[, startrev]])', safe=True)
938 938 def follow(repo, subset, x):
939 939 """
940 940 An alias for ``::.`` (ancestors of the working directory's first parent).
941 941 If pattern is specified, the histories of files matching given
942 942 pattern in the revision given by startrev are followed, including copies.
943 943 """
944 944 return _follow(repo, subset, x, 'follow')
945 945
946 946 @predicate('_followfirst', safe=True)
947 947 def _followfirst(repo, subset, x):
948 948 # ``followfirst([pattern[, startrev]])``
949 949 # Like ``follow([pattern[, startrev]])`` but follows only the first parent
950 950 # of every revisions or files revisions.
951 951 return _follow(repo, subset, x, '_followfirst', followfirst=True)
952 952
953 953 @predicate('followlines(file, fromline:toline[, startrev=., descend=False])',
954 954 safe=True)
955 955 def followlines(repo, subset, x):
956 956 """Changesets modifying `file` in line range ('fromline', 'toline').
957 957
958 958 Line range corresponds to 'file' content at 'startrev' and should hence be
959 959 consistent with file size. If startrev is not specified, working directory's
960 960 parent is used.
961 961
962 962 By default, ancestors of 'startrev' are returned. If 'descend' is True,
963 963 descendants of 'startrev' are returned though renames are (currently) not
964 964 followed in this direction.
965 965 """
966 966 args = getargsdict(x, 'followlines', 'file *lines startrev descend')
967 967 if len(args['lines']) != 1:
968 968 raise error.ParseError(_("followlines requires a line range"))
969 969
970 970 rev = '.'
971 971 if 'startrev' in args:
972 972 revs = getset(repo, fullreposet(repo), args['startrev'])
973 973 if len(revs) != 1:
974 974 raise error.ParseError(
975 975 # i18n: "followlines" is a keyword
976 976 _("followlines expects exactly one revision"))
977 977 rev = revs.last()
978 978
979 979 pat = getstring(args['file'], _("followlines requires a pattern"))
980 980 # i18n: "followlines" is a keyword
981 981 msg = _("followlines expects exactly one file")
982 982 fname = scmutil.parsefollowlinespattern(repo, rev, pat, msg)
983 983 # i18n: "followlines" is a keyword
984 984 lr = getrange(args['lines'][0], _("followlines expects a line range"))
985 985 fromline, toline = [getinteger(a, _("line range bounds must be integers"))
986 986 for a in lr]
987 987 fromline, toline = util.processlinerange(fromline, toline)
988 988
989 989 fctx = repo[rev].filectx(fname)
990 990 descend = False
991 991 if 'descend' in args:
992 992 descend = getboolean(args['descend'],
993 993 # i18n: "descend" is a keyword
994 994 _("descend argument must be a boolean"))
995 995 if descend:
996 996 rs = generatorset(
997 997 (c.rev() for c, _linerange
998 998 in dagop.blockdescendants(fctx, fromline, toline)),
999 999 iterasc=True)
1000 1000 else:
1001 1001 rs = generatorset(
1002 1002 (c.rev() for c, _linerange
1003 1003 in dagop.blockancestors(fctx, fromline, toline)),
1004 1004 iterasc=False)
1005 1005 return subset & rs
1006 1006
1007 1007 @predicate('all()', safe=True)
1008 1008 def getall(repo, subset, x):
1009 1009 """All changesets, the same as ``0:tip``.
1010 1010 """
1011 1011 # i18n: "all" is a keyword
1012 1012 getargs(x, 0, 0, _("all takes no arguments"))
1013 1013 return subset & spanset(repo) # drop "null" if any
1014 1014
1015 1015 @predicate('grep(regex)', weight=10)
1016 1016 def grep(repo, subset, x):
1017 1017 """Like ``keyword(string)`` but accepts a regex. Use ``grep(r'...')``
1018 1018 to ensure special escape characters are handled correctly. Unlike
1019 1019 ``keyword(string)``, the match is case-sensitive.
1020 1020 """
1021 1021 try:
1022 1022 # i18n: "grep" is a keyword
1023 1023 gr = re.compile(getstring(x, _("grep requires a string")))
1024 1024 except re.error as e:
1025 1025 raise error.ParseError(_('invalid match pattern: %s') % e)
1026 1026
1027 1027 def matches(x):
1028 1028 c = repo[x]
1029 1029 for e in c.files() + [c.user(), c.description()]:
1030 1030 if gr.search(e):
1031 1031 return True
1032 1032 return False
1033 1033
1034 1034 return subset.filter(matches, condrepr=('<grep %r>', gr.pattern))
1035 1035
1036 1036 @predicate('_matchfiles', safe=True)
1037 1037 def _matchfiles(repo, subset, x):
1038 1038 # _matchfiles takes a revset list of prefixed arguments:
1039 1039 #
1040 1040 # [p:foo, i:bar, x:baz]
1041 1041 #
1042 1042 # builds a match object from them and filters subset. Allowed
1043 1043 # prefixes are 'p:' for regular patterns, 'i:' for include
1044 1044 # patterns and 'x:' for exclude patterns. Use 'r:' prefix to pass
1045 1045 # a revision identifier, or the empty string to reference the
1046 1046 # working directory, from which the match object is
1047 1047 # initialized. Use 'd:' to set the default matching mode, default
1048 1048 # to 'glob'. At most one 'r:' and 'd:' argument can be passed.
1049 1049
1050 1050 l = getargs(x, 1, -1, "_matchfiles requires at least one argument")
1051 1051 pats, inc, exc = [], [], []
1052 1052 rev, default = None, None
1053 1053 for arg in l:
1054 1054 s = getstring(arg, "_matchfiles requires string arguments")
1055 1055 prefix, value = s[:2], s[2:]
1056 1056 if prefix == 'p:':
1057 1057 pats.append(value)
1058 1058 elif prefix == 'i:':
1059 1059 inc.append(value)
1060 1060 elif prefix == 'x:':
1061 1061 exc.append(value)
1062 1062 elif prefix == 'r:':
1063 1063 if rev is not None:
1064 1064 raise error.ParseError('_matchfiles expected at most one '
1065 1065 'revision')
1066 1066 if value != '': # empty means working directory; leave rev as None
1067 1067 rev = value
1068 1068 elif prefix == 'd:':
1069 1069 if default is not None:
1070 1070 raise error.ParseError('_matchfiles expected at most one '
1071 1071 'default mode')
1072 1072 default = value
1073 1073 else:
1074 1074 raise error.ParseError('invalid _matchfiles prefix: %s' % prefix)
1075 1075 if not default:
1076 1076 default = 'glob'
1077 1077
1078 1078 m = matchmod.match(repo.root, repo.getcwd(), pats, include=inc,
1079 1079 exclude=exc, ctx=repo[rev], default=default)
1080 1080
1081 1081 # This directly read the changelog data as creating changectx for all
1082 1082 # revisions is quite expensive.
1083 1083 getfiles = repo.changelog.readfiles
1084 1084 wdirrev = node.wdirrev
1085 1085 def matches(x):
1086 1086 if x == wdirrev:
1087 1087 files = repo[x].files()
1088 1088 else:
1089 1089 files = getfiles(x)
1090 1090 for f in files:
1091 1091 if m(f):
1092 1092 return True
1093 1093 return False
1094 1094
1095 1095 return subset.filter(matches,
1096 1096 condrepr=('<matchfiles patterns=%r, include=%r '
1097 1097 'exclude=%r, default=%r, rev=%r>',
1098 1098 pats, inc, exc, default, rev))
1099 1099
1100 1100 @predicate('file(pattern)', safe=True, weight=10)
1101 1101 def hasfile(repo, subset, x):
1102 1102 """Changesets affecting files matched by pattern.
1103 1103
1104 1104 For a faster but less accurate result, consider using ``filelog()``
1105 1105 instead.
1106 1106
1107 1107 This predicate uses ``glob:`` as the default kind of pattern.
1108 1108 """
1109 1109 # i18n: "file" is a keyword
1110 1110 pat = getstring(x, _("file requires a pattern"))
1111 1111 return _matchfiles(repo, subset, ('string', 'p:' + pat))
1112 1112
1113 1113 @predicate('head()', safe=True)
1114 1114 def head(repo, subset, x):
1115 1115 """Changeset is a named branch head.
1116 1116 """
1117 1117 # i18n: "head" is a keyword
1118 1118 getargs(x, 0, 0, _("head takes no arguments"))
1119 1119 hs = set()
1120 1120 cl = repo.changelog
1121 1121 for ls in repo.branchmap().itervalues():
1122 1122 hs.update(cl.rev(h) for h in ls)
1123 1123 return subset & baseset(hs)
1124 1124
1125 1125 @predicate('heads(set)', safe=True)
1126 1126 def heads(repo, subset, x):
1127 1127 """Members of set with no children in set.
1128 1128 """
1129 1129 s = getset(repo, subset, x)
1130 1130 ps = parents(repo, subset, x)
1131 1131 return s - ps
1132 1132
1133 1133 @predicate('hidden()', safe=True)
1134 1134 def hidden(repo, subset, x):
1135 1135 """Hidden changesets.
1136 1136 """
1137 1137 # i18n: "hidden" is a keyword
1138 1138 getargs(x, 0, 0, _("hidden takes no arguments"))
1139 1139 hiddenrevs = repoview.filterrevs(repo, 'visible')
1140 1140 return subset & hiddenrevs
1141 1141
1142 1142 @predicate('keyword(string)', safe=True, weight=10)
1143 1143 def keyword(repo, subset, x):
1144 1144 """Search commit message, user name, and names of changed files for
1145 1145 string. The match is case-insensitive.
1146 1146
1147 1147 For a regular expression or case sensitive search of these fields, use
1148 1148 ``grep(regex)``.
1149 1149 """
1150 1150 # i18n: "keyword" is a keyword
1151 1151 kw = encoding.lower(getstring(x, _("keyword requires a string")))
1152 1152
1153 1153 def matches(r):
1154 1154 c = repo[r]
1155 1155 return any(kw in encoding.lower(t)
1156 1156 for t in c.files() + [c.user(), c.description()])
1157 1157
1158 1158 return subset.filter(matches, condrepr=('<keyword %r>', kw))
1159 1159
1160 1160 @predicate('limit(set[, n[, offset]])', safe=True, takeorder=True, weight=0)
1161 1161 def limit(repo, subset, x, order):
1162 1162 """First n members of set, defaulting to 1, starting from offset.
1163 1163 """
1164 1164 args = getargsdict(x, 'limit', 'set n offset')
1165 1165 if 'set' not in args:
1166 1166 # i18n: "limit" is a keyword
1167 1167 raise error.ParseError(_("limit requires one to three arguments"))
1168 1168 # i18n: "limit" is a keyword
1169 1169 lim = getinteger(args.get('n'), _("limit expects a number"), default=1)
1170 1170 if lim < 0:
1171 1171 raise error.ParseError(_("negative number to select"))
1172 1172 # i18n: "limit" is a keyword
1173 1173 ofs = getinteger(args.get('offset'), _("limit expects a number"), default=0)
1174 1174 if ofs < 0:
1175 1175 raise error.ParseError(_("negative offset"))
1176 1176 os = getset(repo, fullreposet(repo), args['set'])
1177 1177 ls = os.slice(ofs, ofs + lim)
1178 1178 if order == followorder and lim > 1:
1179 1179 return subset & ls
1180 1180 return ls & subset
1181 1181
1182 1182 @predicate('last(set, [n])', safe=True, takeorder=True)
1183 1183 def last(repo, subset, x, order):
1184 1184 """Last n members of set, defaulting to 1.
1185 1185 """
1186 1186 # i18n: "last" is a keyword
1187 1187 l = getargs(x, 1, 2, _("last requires one or two arguments"))
1188 1188 lim = 1
1189 1189 if len(l) == 2:
1190 1190 # i18n: "last" is a keyword
1191 1191 lim = getinteger(l[1], _("last expects a number"))
1192 1192 if lim < 0:
1193 1193 raise error.ParseError(_("negative number to select"))
1194 1194 os = getset(repo, fullreposet(repo), l[0])
1195 1195 os.reverse()
1196 1196 ls = os.slice(0, lim)
1197 1197 if order == followorder and lim > 1:
1198 1198 return subset & ls
1199 1199 ls.reverse()
1200 1200 return ls & subset
1201 1201
1202 1202 @predicate('max(set)', safe=True)
1203 1203 def maxrev(repo, subset, x):
1204 1204 """Changeset with highest revision number in set.
1205 1205 """
1206 1206 os = getset(repo, fullreposet(repo), x)
1207 1207 try:
1208 1208 m = os.max()
1209 1209 if m in subset:
1210 1210 return baseset([m], datarepr=('<max %r, %r>', subset, os))
1211 1211 except ValueError:
1212 1212 # os.max() throws a ValueError when the collection is empty.
1213 1213 # Same as python's max().
1214 1214 pass
1215 1215 return baseset(datarepr=('<max %r, %r>', subset, os))
1216 1216
1217 1217 @predicate('merge()', safe=True)
1218 1218 def merge(repo, subset, x):
1219 1219 """Changeset is a merge changeset.
1220 1220 """
1221 1221 # i18n: "merge" is a keyword
1222 1222 getargs(x, 0, 0, _("merge takes no arguments"))
1223 1223 cl = repo.changelog
1224 1224 return subset.filter(lambda r: cl.parentrevs(r)[1] != -1,
1225 1225 condrepr='<merge>')
1226 1226
1227 1227 @predicate('branchpoint()', safe=True)
1228 1228 def branchpoint(repo, subset, x):
1229 1229 """Changesets with more than one child.
1230 1230 """
1231 1231 # i18n: "branchpoint" is a keyword
1232 1232 getargs(x, 0, 0, _("branchpoint takes no arguments"))
1233 1233 cl = repo.changelog
1234 1234 if not subset:
1235 1235 return baseset()
1236 1236 # XXX this should be 'parentset.min()' assuming 'parentset' is a smartset
1237 1237 # (and if it is not, it should.)
1238 1238 baserev = min(subset)
1239 1239 parentscount = [0]*(len(repo) - baserev)
1240 1240 for r in cl.revs(start=baserev + 1):
1241 1241 for p in cl.parentrevs(r):
1242 1242 if p >= baserev:
1243 1243 parentscount[p - baserev] += 1
1244 1244 return subset.filter(lambda r: parentscount[r - baserev] > 1,
1245 1245 condrepr='<branchpoint>')
1246 1246
1247 1247 @predicate('min(set)', safe=True)
1248 1248 def minrev(repo, subset, x):
1249 1249 """Changeset with lowest revision number in set.
1250 1250 """
1251 1251 os = getset(repo, fullreposet(repo), x)
1252 1252 try:
1253 1253 m = os.min()
1254 1254 if m in subset:
1255 1255 return baseset([m], datarepr=('<min %r, %r>', subset, os))
1256 1256 except ValueError:
1257 1257 # os.min() throws a ValueError when the collection is empty.
1258 1258 # Same as python's min().
1259 1259 pass
1260 1260 return baseset(datarepr=('<min %r, %r>', subset, os))
1261 1261
1262 1262 @predicate('modifies(pattern)', safe=True, weight=30)
1263 1263 def modifies(repo, subset, x):
1264 1264 """Changesets modifying files matched by pattern.
1265 1265
1266 1266 The pattern without explicit kind like ``glob:`` is expected to be
1267 1267 relative to the current directory and match against a file or a
1268 1268 directory.
1269 1269 """
1270 1270 # i18n: "modifies" is a keyword
1271 1271 pat = getstring(x, _("modifies requires a pattern"))
1272 1272 return checkstatus(repo, subset, pat, 0)
1273 1273
1274 1274 @predicate('named(namespace)')
1275 1275 def named(repo, subset, x):
1276 1276 """The changesets in a given namespace.
1277 1277
1278 1278 Pattern matching is supported for `namespace`. See
1279 1279 :hg:`help revisions.patterns`.
1280 1280 """
1281 1281 # i18n: "named" is a keyword
1282 1282 args = getargs(x, 1, 1, _('named requires a namespace argument'))
1283 1283
1284 1284 ns = getstring(args[0],
1285 1285 # i18n: "named" is a keyword
1286 1286 _('the argument to named must be a string'))
1287 1287 kind, pattern, matcher = util.stringmatcher(ns)
1288 1288 namespaces = set()
1289 1289 if kind == 'literal':
1290 1290 if pattern not in repo.names:
1291 1291 raise error.RepoLookupError(_("namespace '%s' does not exist")
1292 1292 % ns)
1293 1293 namespaces.add(repo.names[pattern])
1294 1294 else:
1295 1295 for name, ns in repo.names.iteritems():
1296 1296 if matcher(name):
1297 1297 namespaces.add(ns)
1298 1298 if not namespaces:
1299 1299 raise error.RepoLookupError(_("no namespace exists"
1300 1300 " that match '%s'") % pattern)
1301 1301
1302 1302 names = set()
1303 1303 for ns in namespaces:
1304 1304 for name in ns.listnames(repo):
1305 1305 if name not in ns.deprecated:
1306 1306 names.update(repo[n].rev() for n in ns.nodes(repo, name))
1307 1307
1308 1308 names -= {node.nullrev}
1309 1309 return subset & names
1310 1310
1311 1311 @predicate('id(string)', safe=True)
1312 1312 def node_(repo, subset, x):
1313 1313 """Revision non-ambiguously specified by the given hex string prefix.
1314 1314 """
1315 1315 # i18n: "id" is a keyword
1316 1316 l = getargs(x, 1, 1, _("id requires one argument"))
1317 1317 # i18n: "id" is a keyword
1318 1318 n = getstring(l[0], _("id requires a string"))
1319 1319 if len(n) == 40:
1320 1320 try:
1321 1321 rn = repo.changelog.rev(node.bin(n))
1322 1322 except error.WdirUnsupported:
1323 1323 rn = node.wdirrev
1324 1324 except (LookupError, TypeError):
1325 1325 rn = None
1326 1326 else:
1327 1327 rn = None
1328 1328 try:
1329 1329 pm = repo.changelog._partialmatch(n)
1330 1330 if pm is not None:
1331 1331 rn = repo.changelog.rev(pm)
1332 1332 except error.WdirUnsupported:
1333 1333 rn = node.wdirrev
1334 1334
1335 1335 if rn is None:
1336 1336 return baseset()
1337 1337 result = baseset([rn])
1338 1338 return result & subset
1339 1339
1340 1340 @predicate('obsolete()', safe=True)
1341 1341 def obsolete(repo, subset, x):
1342 1342 """Mutable changeset with a newer version."""
1343 1343 # i18n: "obsolete" is a keyword
1344 1344 getargs(x, 0, 0, _("obsolete takes no arguments"))
1345 1345 obsoletes = obsmod.getrevs(repo, 'obsolete')
1346 1346 return subset & obsoletes
1347 1347
1348 1348 @predicate('only(set, [set])', safe=True)
1349 1349 def only(repo, subset, x):
1350 1350 """Changesets that are ancestors of the first set that are not ancestors
1351 1351 of any other head in the repo. If a second set is specified, the result
1352 1352 is ancestors of the first set that are not ancestors of the second set
1353 1353 (i.e. ::<set1> - ::<set2>).
1354 1354 """
1355 1355 cl = repo.changelog
1356 1356 # i18n: "only" is a keyword
1357 1357 args = getargs(x, 1, 2, _('only takes one or two arguments'))
1358 1358 include = getset(repo, fullreposet(repo), args[0])
1359 1359 if len(args) == 1:
1360 1360 if not include:
1361 1361 return baseset()
1362 1362
1363 1363 descendants = set(dagop.revdescendants(repo, include, False))
1364 1364 exclude = [rev for rev in cl.headrevs()
1365 1365 if not rev in descendants and not rev in include]
1366 1366 else:
1367 1367 exclude = getset(repo, fullreposet(repo), args[1])
1368 1368
1369 1369 results = set(cl.findmissingrevs(common=exclude, heads=include))
1370 1370 # XXX we should turn this into a baseset instead of a set, smartset may do
1371 1371 # some optimizations from the fact this is a baseset.
1372 1372 return subset & results
1373 1373
1374 1374 @predicate('origin([set])', safe=True)
1375 1375 def origin(repo, subset, x):
1376 1376 """
1377 1377 Changesets that were specified as a source for the grafts, transplants or
1378 1378 rebases that created the given revisions. Omitting the optional set is the
1379 1379 same as passing all(). If a changeset created by these operations is itself
1380 1380 specified as a source for one of these operations, only the source changeset
1381 1381 for the first operation is selected.
1382 1382 """
1383 1383 if x is not None:
1384 1384 dests = getset(repo, fullreposet(repo), x)
1385 1385 else:
1386 1386 dests = fullreposet(repo)
1387 1387
1388 1388 def _firstsrc(rev):
1389 1389 src = _getrevsource(repo, rev)
1390 1390 if src is None:
1391 1391 return None
1392 1392
1393 1393 while True:
1394 1394 prev = _getrevsource(repo, src)
1395 1395
1396 1396 if prev is None:
1397 1397 return src
1398 1398 src = prev
1399 1399
1400 1400 o = {_firstsrc(r) for r in dests}
1401 1401 o -= {None}
1402 1402 # XXX we should turn this into a baseset instead of a set, smartset may do
1403 1403 # some optimizations from the fact this is a baseset.
1404 1404 return subset & o
1405 1405
1406 1406 @predicate('outgoing([path])', safe=False, weight=10)
1407 1407 def outgoing(repo, subset, x):
1408 1408 """Changesets not found in the specified destination repository, or the
1409 1409 default push location.
1410 1410 """
1411 1411 # Avoid cycles.
1412 1412 from . import (
1413 1413 discovery,
1414 1414 hg,
1415 1415 )
1416 1416 # i18n: "outgoing" is a keyword
1417 1417 l = getargs(x, 0, 1, _("outgoing takes one or no arguments"))
1418 1418 # i18n: "outgoing" is a keyword
1419 1419 dest = l and getstring(l[0], _("outgoing requires a repository path")) or ''
1420 1420 dest = repo.ui.expandpath(dest or 'default-push', dest or 'default')
1421 1421 dest, branches = hg.parseurl(dest)
1422 1422 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1423 1423 if revs:
1424 1424 revs = [repo.lookup(rev) for rev in revs]
1425 1425 other = hg.peer(repo, {}, dest)
1426 1426 repo.ui.pushbuffer()
1427 1427 outgoing = discovery.findcommonoutgoing(repo, other, onlyheads=revs)
1428 1428 repo.ui.popbuffer()
1429 1429 cl = repo.changelog
1430 1430 o = {cl.rev(r) for r in outgoing.missing}
1431 1431 return subset & o
1432 1432
1433 1433 @predicate('p1([set])', safe=True)
1434 1434 def p1(repo, subset, x):
1435 1435 """First parent of changesets in set, or the working directory.
1436 1436 """
1437 1437 if x is None:
1438 1438 p = repo[x].p1().rev()
1439 1439 if p >= 0:
1440 1440 return subset & baseset([p])
1441 1441 return baseset()
1442 1442
1443 1443 ps = set()
1444 1444 cl = repo.changelog
1445 1445 for r in getset(repo, fullreposet(repo), x):
1446 1446 try:
1447 1447 ps.add(cl.parentrevs(r)[0])
1448 1448 except error.WdirUnsupported:
1449 1449 ps.add(repo[r].parents()[0].rev())
1450 1450 ps -= {node.nullrev}
1451 1451 # XXX we should turn this into a baseset instead of a set, smartset may do
1452 1452 # some optimizations from the fact this is a baseset.
1453 1453 return subset & ps
1454 1454
1455 1455 @predicate('p2([set])', safe=True)
1456 1456 def p2(repo, subset, x):
1457 1457 """Second parent of changesets in set, or the working directory.
1458 1458 """
1459 1459 if x is None:
1460 1460 ps = repo[x].parents()
1461 1461 try:
1462 1462 p = ps[1].rev()
1463 1463 if p >= 0:
1464 1464 return subset & baseset([p])
1465 1465 return baseset()
1466 1466 except IndexError:
1467 1467 return baseset()
1468 1468
1469 1469 ps = set()
1470 1470 cl = repo.changelog
1471 1471 for r in getset(repo, fullreposet(repo), x):
1472 1472 try:
1473 1473 ps.add(cl.parentrevs(r)[1])
1474 1474 except error.WdirUnsupported:
1475 1475 parents = repo[r].parents()
1476 1476 if len(parents) == 2:
1477 1477 ps.add(parents[1])
1478 1478 ps -= {node.nullrev}
1479 1479 # XXX we should turn this into a baseset instead of a set, smartset may do
1480 1480 # some optimizations from the fact this is a baseset.
1481 1481 return subset & ps
1482 1482
1483 1483 def parentpost(repo, subset, x, order):
1484 1484 return p1(repo, subset, x)
1485 1485
1486 1486 @predicate('parents([set])', safe=True)
1487 1487 def parents(repo, subset, x):
1488 1488 """
1489 1489 The set of all parents for all changesets in set, or the working directory.
1490 1490 """
1491 1491 if x is None:
1492 1492 ps = set(p.rev() for p in repo[x].parents())
1493 1493 else:
1494 1494 ps = set()
1495 1495 cl = repo.changelog
1496 1496 up = ps.update
1497 1497 parentrevs = cl.parentrevs
1498 1498 for r in getset(repo, fullreposet(repo), x):
1499 1499 try:
1500 1500 up(parentrevs(r))
1501 1501 except error.WdirUnsupported:
1502 1502 up(p.rev() for p in repo[r].parents())
1503 1503 ps -= {node.nullrev}
1504 1504 return subset & ps
1505 1505
1506 1506 def _phase(repo, subset, *targets):
1507 1507 """helper to select all rev in <targets> phases"""
1508 1508 s = repo._phasecache.getrevset(repo, targets)
1509 1509 return subset & s
1510 1510
1511 1511 @predicate('draft()', safe=True)
1512 1512 def draft(repo, subset, x):
1513 1513 """Changeset in draft phase."""
1514 1514 # i18n: "draft" is a keyword
1515 1515 getargs(x, 0, 0, _("draft takes no arguments"))
1516 1516 target = phases.draft
1517 1517 return _phase(repo, subset, target)
1518 1518
1519 1519 @predicate('secret()', safe=True)
1520 1520 def secret(repo, subset, x):
1521 1521 """Changeset in secret phase."""
1522 1522 # i18n: "secret" is a keyword
1523 1523 getargs(x, 0, 0, _("secret takes no arguments"))
1524 1524 target = phases.secret
1525 1525 return _phase(repo, subset, target)
1526 1526
1527 1527 def parentspec(repo, subset, x, n, order):
1528 1528 """``set^0``
1529 1529 The set.
1530 1530 ``set^1`` (or ``set^``), ``set^2``
1531 1531 First or second parent, respectively, of all changesets in set.
1532 1532 """
1533 1533 try:
1534 1534 n = int(n[1])
1535 1535 if n not in (0, 1, 2):
1536 1536 raise ValueError
1537 1537 except (TypeError, ValueError):
1538 1538 raise error.ParseError(_("^ expects a number 0, 1, or 2"))
1539 1539 ps = set()
1540 1540 cl = repo.changelog
1541 1541 for r in getset(repo, fullreposet(repo), x):
1542 1542 if n == 0:
1543 1543 ps.add(r)
1544 1544 elif n == 1:
1545 1545 try:
1546 1546 ps.add(cl.parentrevs(r)[0])
1547 1547 except error.WdirUnsupported:
1548 1548 ps.add(repo[r].parents()[0].rev())
1549 1549 else:
1550 1550 try:
1551 1551 parents = cl.parentrevs(r)
1552 1552 if parents[1] != node.nullrev:
1553 1553 ps.add(parents[1])
1554 1554 except error.WdirUnsupported:
1555 1555 parents = repo[r].parents()
1556 1556 if len(parents) == 2:
1557 1557 ps.add(parents[1].rev())
1558 1558 return subset & ps
1559 1559
1560 1560 @predicate('present(set)', safe=True, takeorder=True)
1561 1561 def present(repo, subset, x, order):
1562 1562 """An empty set, if any revision in set isn't found; otherwise,
1563 1563 all revisions in set.
1564 1564
1565 1565 If any of specified revisions is not present in the local repository,
1566 1566 the query is normally aborted. But this predicate allows the query
1567 1567 to continue even in such cases.
1568 1568 """
1569 1569 try:
1570 1570 return getset(repo, subset, x, order)
1571 1571 except error.RepoLookupError:
1572 1572 return baseset()
1573 1573
1574 1574 # for internal use
1575 1575 @predicate('_notpublic', safe=True)
1576 1576 def _notpublic(repo, subset, x):
1577 1577 getargs(x, 0, 0, "_notpublic takes no arguments")
1578 1578 return _phase(repo, subset, phases.draft, phases.secret)
1579 1579
1580 1580 # for internal use
1581 1581 @predicate('_phaseandancestors(phasename, set)', safe=True)
1582 1582 def _phaseandancestors(repo, subset, x):
1583 1583 # equivalent to (phasename() & ancestors(set)) but more efficient
1584 1584 # phasename could be one of 'draft', 'secret', or '_notpublic'
1585 1585 args = getargs(x, 2, 2, "_phaseandancestors requires two arguments")
1586 1586 phasename = getsymbol(args[0])
1587 1587 s = getset(repo, fullreposet(repo), args[1])
1588 1588
1589 1589 draft = phases.draft
1590 1590 secret = phases.secret
1591 1591 phasenamemap = {
1592 1592 '_notpublic': draft,
1593 1593 'draft': draft, # follow secret's ancestors
1594 1594 'secret': secret,
1595 1595 }
1596 1596 if phasename not in phasenamemap:
1597 1597 raise error.ParseError('%r is not a valid phasename' % phasename)
1598 1598
1599 1599 minimalphase = phasenamemap[phasename]
1600 1600 getphase = repo._phasecache.phase
1601 1601
1602 1602 def cutfunc(rev):
1603 1603 return getphase(repo, rev) < minimalphase
1604 1604
1605 1605 revs = dagop.revancestors(repo, s, cutfunc=cutfunc)
1606 1606
1607 1607 if phasename == 'draft': # need to remove secret changesets
1608 1608 revs = revs.filter(lambda r: getphase(repo, r) == draft)
1609 1609 return subset & revs
1610 1610
1611 1611 @predicate('public()', safe=True)
1612 1612 def public(repo, subset, x):
1613 1613 """Changeset in public phase."""
1614 1614 # i18n: "public" is a keyword
1615 1615 getargs(x, 0, 0, _("public takes no arguments"))
1616 1616 phase = repo._phasecache.phase
1617 1617 target = phases.public
1618 1618 condition = lambda r: phase(repo, r) == target
1619 1619 return subset.filter(condition, condrepr=('<phase %r>', target),
1620 1620 cache=False)
1621 1621
1622 1622 @predicate('remote([id [,path]])', safe=False)
1623 1623 def remote(repo, subset, x):
1624 1624 """Local revision that corresponds to the given identifier in a
1625 1625 remote repository, if present. Here, the '.' identifier is a
1626 1626 synonym for the current local branch.
1627 1627 """
1628 1628
1629 1629 from . import hg # avoid start-up nasties
1630 1630 # i18n: "remote" is a keyword
1631 1631 l = getargs(x, 0, 2, _("remote takes zero, one, or two arguments"))
1632 1632
1633 1633 q = '.'
1634 1634 if len(l) > 0:
1635 1635 # i18n: "remote" is a keyword
1636 1636 q = getstring(l[0], _("remote requires a string id"))
1637 1637 if q == '.':
1638 1638 q = repo['.'].branch()
1639 1639
1640 1640 dest = ''
1641 1641 if len(l) > 1:
1642 1642 # i18n: "remote" is a keyword
1643 1643 dest = getstring(l[1], _("remote requires a repository path"))
1644 1644 dest = repo.ui.expandpath(dest or 'default')
1645 1645 dest, branches = hg.parseurl(dest)
1646 1646 revs, checkout = hg.addbranchrevs(repo, repo, branches, [])
1647 1647 if revs:
1648 1648 revs = [repo.lookup(rev) for rev in revs]
1649 1649 other = hg.peer(repo, {}, dest)
1650 1650 n = other.lookup(q)
1651 1651 if n in repo:
1652 1652 r = repo[n].rev()
1653 1653 if r in subset:
1654 1654 return baseset([r])
1655 1655 return baseset()
1656 1656
1657 1657 @predicate('removes(pattern)', safe=True, weight=30)
1658 1658 def removes(repo, subset, x):
1659 1659 """Changesets which remove files matching pattern.
1660 1660
1661 1661 The pattern without explicit kind like ``glob:`` is expected to be
1662 1662 relative to the current directory and match against a file or a
1663 1663 directory.
1664 1664 """
1665 1665 # i18n: "removes" is a keyword
1666 1666 pat = getstring(x, _("removes requires a pattern"))
1667 1667 return checkstatus(repo, subset, pat, 2)
1668 1668
1669 1669 @predicate('rev(number)', safe=True)
1670 1670 def rev(repo, subset, x):
1671 1671 """Revision with the given numeric identifier.
1672 1672 """
1673 1673 # i18n: "rev" is a keyword
1674 1674 l = getargs(x, 1, 1, _("rev requires one argument"))
1675 1675 try:
1676 1676 # i18n: "rev" is a keyword
1677 1677 l = int(getstring(l[0], _("rev requires a number")))
1678 1678 except (TypeError, ValueError):
1679 1679 # i18n: "rev" is a keyword
1680 1680 raise error.ParseError(_("rev expects a number"))
1681 1681 if l not in repo.changelog and l not in (node.nullrev, node.wdirrev):
1682 1682 return baseset()
1683 1683 return subset & baseset([l])
1684 1684
1685 1685 @predicate('matching(revision [, field])', safe=True)
1686 1686 def matching(repo, subset, x):
1687 1687 """Changesets in which a given set of fields match the set of fields in the
1688 1688 selected revision or set.
1689 1689
1690 1690 To match more than one field pass the list of fields to match separated
1691 1691 by spaces (e.g. ``author description``).
1692 1692
1693 1693 Valid fields are most regular revision fields and some special fields.
1694 1694
1695 1695 Regular revision fields are ``description``, ``author``, ``branch``,
1696 1696 ``date``, ``files``, ``phase``, ``parents``, ``substate``, ``user``
1697 1697 and ``diff``.
1698 1698 Note that ``author`` and ``user`` are synonyms. ``diff`` refers to the
1699 1699 contents of the revision. Two revisions matching their ``diff`` will
1700 1700 also match their ``files``.
1701 1701
1702 1702 Special fields are ``summary`` and ``metadata``:
1703 1703 ``summary`` matches the first line of the description.
1704 1704 ``metadata`` is equivalent to matching ``description user date``
1705 1705 (i.e. it matches the main metadata fields).
1706 1706
1707 1707 ``metadata`` is the default field which is used when no fields are
1708 1708 specified. You can match more than one field at a time.
1709 1709 """
1710 1710 # i18n: "matching" is a keyword
1711 1711 l = getargs(x, 1, 2, _("matching takes 1 or 2 arguments"))
1712 1712
1713 1713 revs = getset(repo, fullreposet(repo), l[0])
1714 1714
1715 1715 fieldlist = ['metadata']
1716 1716 if len(l) > 1:
1717 1717 fieldlist = getstring(l[1],
1718 1718 # i18n: "matching" is a keyword
1719 1719 _("matching requires a string "
1720 1720 "as its second argument")).split()
1721 1721
1722 1722 # Make sure that there are no repeated fields,
1723 1723 # expand the 'special' 'metadata' field type
1724 1724 # and check the 'files' whenever we check the 'diff'
1725 1725 fields = []
1726 1726 for field in fieldlist:
1727 1727 if field == 'metadata':
1728 1728 fields += ['user', 'description', 'date']
1729 1729 elif field == 'diff':
1730 1730 # a revision matching the diff must also match the files
1731 1731 # since matching the diff is very costly, make sure to
1732 1732 # also match the files first
1733 1733 fields += ['files', 'diff']
1734 1734 else:
1735 1735 if field == 'author':
1736 1736 field = 'user'
1737 1737 fields.append(field)
1738 1738 fields = set(fields)
1739 1739 if 'summary' in fields and 'description' in fields:
1740 1740 # If a revision matches its description it also matches its summary
1741 1741 fields.discard('summary')
1742 1742
1743 1743 # We may want to match more than one field
1744 1744 # Not all fields take the same amount of time to be matched
1745 1745 # Sort the selected fields in order of increasing matching cost
1746 1746 fieldorder = ['phase', 'parents', 'user', 'date', 'branch', 'summary',
1747 1747 'files', 'description', 'substate', 'diff']
1748 1748 def fieldkeyfunc(f):
1749 1749 try:
1750 1750 return fieldorder.index(f)
1751 1751 except ValueError:
1752 1752 # assume an unknown field is very costly
1753 1753 return len(fieldorder)
1754 1754 fields = list(fields)
1755 1755 fields.sort(key=fieldkeyfunc)
1756 1756
1757 1757 # Each field will be matched with its own "getfield" function
1758 1758 # which will be added to the getfieldfuncs array of functions
1759 1759 getfieldfuncs = []
1760 1760 _funcs = {
1761 1761 'user': lambda r: repo[r].user(),
1762 1762 'branch': lambda r: repo[r].branch(),
1763 1763 'date': lambda r: repo[r].date(),
1764 1764 'description': lambda r: repo[r].description(),
1765 1765 'files': lambda r: repo[r].files(),
1766 1766 'parents': lambda r: repo[r].parents(),
1767 1767 'phase': lambda r: repo[r].phase(),
1768 1768 'substate': lambda r: repo[r].substate,
1769 1769 'summary': lambda r: repo[r].description().splitlines()[0],
1770 1770 'diff': lambda r: list(repo[r].diff(git=True),)
1771 1771 }
1772 1772 for info in fields:
1773 1773 getfield = _funcs.get(info, None)
1774 1774 if getfield is None:
1775 1775 raise error.ParseError(
1776 1776 # i18n: "matching" is a keyword
1777 1777 _("unexpected field name passed to matching: %s") % info)
1778 1778 getfieldfuncs.append(getfield)
1779 1779 # convert the getfield array of functions into a "getinfo" function
1780 1780 # which returns an array of field values (or a single value if there
1781 1781 # is only one field to match)
1782 1782 getinfo = lambda r: [f(r) for f in getfieldfuncs]
1783 1783
1784 1784 def matches(x):
1785 1785 for rev in revs:
1786 1786 target = getinfo(rev)
1787 1787 match = True
1788 1788 for n, f in enumerate(getfieldfuncs):
1789 1789 if target[n] != f(x):
1790 1790 match = False
1791 1791 if match:
1792 1792 return True
1793 1793 return False
1794 1794
1795 1795 return subset.filter(matches, condrepr=('<matching%r %r>', fields, revs))
1796 1796
1797 1797 @predicate('reverse(set)', safe=True, takeorder=True, weight=0)
1798 1798 def reverse(repo, subset, x, order):
1799 1799 """Reverse order of set.
1800 1800 """
1801 1801 l = getset(repo, subset, x, order)
1802 1802 if order == defineorder:
1803 1803 l.reverse()
1804 1804 return l
1805 1805
1806 1806 @predicate('roots(set)', safe=True)
1807 1807 def roots(repo, subset, x):
1808 1808 """Changesets in set with no parent changeset in set.
1809 1809 """
1810 1810 s = getset(repo, fullreposet(repo), x)
1811 1811 parents = repo.changelog.parentrevs
1812 1812 def filter(r):
1813 1813 for p in parents(r):
1814 1814 if 0 <= p and p in s:
1815 1815 return False
1816 1816 return True
1817 1817 return subset & s.filter(filter, condrepr='<roots>')
1818 1818
1819 1819 _sortkeyfuncs = {
1820 1820 'rev': lambda c: c.rev(),
1821 1821 'branch': lambda c: c.branch(),
1822 1822 'desc': lambda c: c.description(),
1823 1823 'user': lambda c: c.user(),
1824 1824 'author': lambda c: c.user(),
1825 1825 'date': lambda c: c.date()[0],
1826 1826 }
1827 1827
1828 1828 def _getsortargs(x):
1829 1829 """Parse sort options into (set, [(key, reverse)], opts)"""
1830 1830 args = getargsdict(x, 'sort', 'set keys topo.firstbranch')
1831 1831 if 'set' not in args:
1832 1832 # i18n: "sort" is a keyword
1833 1833 raise error.ParseError(_('sort requires one or two arguments'))
1834 1834 keys = "rev"
1835 1835 if 'keys' in args:
1836 1836 # i18n: "sort" is a keyword
1837 1837 keys = getstring(args['keys'], _("sort spec must be a string"))
1838 1838
1839 1839 keyflags = []
1840 1840 for k in keys.split():
1841 1841 fk = k
1842 1842 reverse = (k[0] == '-')
1843 1843 if reverse:
1844 1844 k = k[1:]
1845 1845 if k not in _sortkeyfuncs and k != 'topo':
1846 1846 raise error.ParseError(_("unknown sort key %r") % fk)
1847 1847 keyflags.append((k, reverse))
1848 1848
1849 1849 if len(keyflags) > 1 and any(k == 'topo' for k, reverse in keyflags):
1850 1850 # i18n: "topo" is a keyword
1851 1851 raise error.ParseError(_('topo sort order cannot be combined '
1852 1852 'with other sort keys'))
1853 1853
1854 1854 opts = {}
1855 1855 if 'topo.firstbranch' in args:
1856 1856 if any(k == 'topo' for k, reverse in keyflags):
1857 1857 opts['topo.firstbranch'] = args['topo.firstbranch']
1858 1858 else:
1859 1859 # i18n: "topo" and "topo.firstbranch" are keywords
1860 1860 raise error.ParseError(_('topo.firstbranch can only be used '
1861 1861 'when using the topo sort key'))
1862 1862
1863 1863 return args['set'], keyflags, opts
1864 1864
1865 1865 @predicate('sort(set[, [-]key... [, ...]])', safe=True, takeorder=True,
1866 1866 weight=10)
1867 1867 def sort(repo, subset, x, order):
1868 1868 """Sort set by keys. The default sort order is ascending, specify a key
1869 1869 as ``-key`` to sort in descending order.
1870 1870
1871 1871 The keys can be:
1872 1872
1873 1873 - ``rev`` for the revision number,
1874 1874 - ``branch`` for the branch name,
1875 1875 - ``desc`` for the commit message (description),
1876 1876 - ``user`` for user name (``author`` can be used as an alias),
1877 1877 - ``date`` for the commit date
1878 1878 - ``topo`` for a reverse topographical sort
1879 1879
1880 1880 The ``topo`` sort order cannot be combined with other sort keys. This sort
1881 1881 takes one optional argument, ``topo.firstbranch``, which takes a revset that
1882 1882 specifies what topographical branches to prioritize in the sort.
1883 1883
1884 1884 """
1885 1885 s, keyflags, opts = _getsortargs(x)
1886 1886 revs = getset(repo, subset, s, order)
1887 1887
1888 1888 if not keyflags or order != defineorder:
1889 1889 return revs
1890 1890 if len(keyflags) == 1 and keyflags[0][0] == "rev":
1891 1891 revs.sort(reverse=keyflags[0][1])
1892 1892 return revs
1893 1893 elif keyflags[0][0] == "topo":
1894 1894 firstbranch = ()
1895 1895 if 'topo.firstbranch' in opts:
1896 1896 firstbranch = getset(repo, subset, opts['topo.firstbranch'])
1897 1897 revs = baseset(dagop.toposort(revs, repo.changelog.parentrevs,
1898 1898 firstbranch),
1899 1899 istopo=True)
1900 1900 if keyflags[0][1]:
1901 1901 revs.reverse()
1902 1902 return revs
1903 1903
1904 1904 # sort() is guaranteed to be stable
1905 1905 ctxs = [repo[r] for r in revs]
1906 1906 for k, reverse in reversed(keyflags):
1907 1907 ctxs.sort(key=_sortkeyfuncs[k], reverse=reverse)
1908 1908 return baseset([c.rev() for c in ctxs])
1909 1909
1910 1910 @predicate('subrepo([pattern])')
1911 1911 def subrepo(repo, subset, x):
1912 1912 """Changesets that add, modify or remove the given subrepo. If no subrepo
1913 1913 pattern is named, any subrepo changes are returned.
1914 1914 """
1915 1915 # i18n: "subrepo" is a keyword
1916 1916 args = getargs(x, 0, 1, _('subrepo takes at most one argument'))
1917 1917 pat = None
1918 1918 if len(args) != 0:
1919 1919 pat = getstring(args[0], _("subrepo requires a pattern"))
1920 1920
1921 1921 m = matchmod.exact(repo.root, repo.root, ['.hgsubstate'])
1922 1922
1923 1923 def submatches(names):
1924 1924 k, p, m = util.stringmatcher(pat)
1925 1925 for name in names:
1926 1926 if m(name):
1927 1927 yield name
1928 1928
1929 1929 def matches(x):
1930 1930 c = repo[x]
1931 1931 s = repo.status(c.p1().node(), c.node(), match=m)
1932 1932
1933 1933 if pat is None:
1934 1934 return s.added or s.modified or s.removed
1935 1935
1936 1936 if s.added:
1937 1937 return any(submatches(c.substate.keys()))
1938 1938
1939 1939 if s.modified:
1940 1940 subs = set(c.p1().substate.keys())
1941 1941 subs.update(c.substate.keys())
1942 1942
1943 1943 for path in submatches(subs):
1944 1944 if c.p1().substate.get(path) != c.substate.get(path):
1945 1945 return True
1946 1946
1947 1947 if s.removed:
1948 1948 return any(submatches(c.p1().substate.keys()))
1949 1949
1950 1950 return False
1951 1951
1952 1952 return subset.filter(matches, condrepr=('<subrepo %r>', pat))
1953 1953
1954 1954 def _mapbynodefunc(repo, s, f):
1955 1955 """(repo, smartset, [node] -> [node]) -> smartset
1956 1956
1957 1957 Helper method to map a smartset to another smartset given a function only
1958 1958 talking about nodes. Handles converting between rev numbers and nodes, and
1959 1959 filtering.
1960 1960 """
1961 1961 cl = repo.unfiltered().changelog
1962 1962 torev = cl.rev
1963 1963 tonode = cl.node
1964 1964 nodemap = cl.nodemap
1965 1965 result = set(torev(n) for n in f(tonode(r) for r in s) if n in nodemap)
1966 1966 return smartset.baseset(result - repo.changelog.filteredrevs)
1967 1967
1968 1968 @predicate('successors(set)', safe=True)
1969 1969 def successors(repo, subset, x):
1970 1970 """All successors for set, including the given set themselves"""
1971 1971 s = getset(repo, fullreposet(repo), x)
1972 1972 f = lambda nodes: obsutil.allsuccessors(repo.obsstore, nodes)
1973 1973 d = _mapbynodefunc(repo, s, f)
1974 1974 return subset & d
1975 1975
1976 1976 def _substringmatcher(pattern, casesensitive=True):
1977 1977 kind, pattern, matcher = util.stringmatcher(pattern,
1978 1978 casesensitive=casesensitive)
1979 1979 if kind == 'literal':
1980 1980 if not casesensitive:
1981 1981 pattern = encoding.lower(pattern)
1982 1982 matcher = lambda s: pattern in encoding.lower(s)
1983 1983 else:
1984 1984 matcher = lambda s: pattern in s
1985 1985 return kind, pattern, matcher
1986 1986
1987 1987 @predicate('tag([name])', safe=True)
1988 1988 def tag(repo, subset, x):
1989 1989 """The specified tag by name, or all tagged revisions if no name is given.
1990 1990
1991 1991 Pattern matching is supported for `name`. See
1992 1992 :hg:`help revisions.patterns`.
1993 1993 """
1994 1994 # i18n: "tag" is a keyword
1995 1995 args = getargs(x, 0, 1, _("tag takes one or no arguments"))
1996 1996 cl = repo.changelog
1997 1997 if args:
1998 1998 pattern = getstring(args[0],
1999 1999 # i18n: "tag" is a keyword
2000 2000 _('the argument to tag must be a string'))
2001 2001 kind, pattern, matcher = util.stringmatcher(pattern)
2002 2002 if kind == 'literal':
2003 2003 # avoid resolving all tags
2004 2004 tn = repo._tagscache.tags.get(pattern, None)
2005 2005 if tn is None:
2006 2006 raise error.RepoLookupError(_("tag '%s' does not exist")
2007 2007 % pattern)
2008 2008 s = {repo[tn].rev()}
2009 2009 else:
2010 2010 s = {cl.rev(n) for t, n in repo.tagslist() if matcher(t)}
2011 2011 else:
2012 2012 s = {cl.rev(n) for t, n in repo.tagslist() if t != 'tip'}
2013 2013 return subset & s
2014 2014
2015 2015 @predicate('tagged', safe=True)
2016 2016 def tagged(repo, subset, x):
2017 2017 return tag(repo, subset, x)
2018 2018
2019 2019 @predicate('unstable()', safe=True)
2020 2020 def unstable(repo, subset, x):
2021 2021 msg = ("'unstable()' is deprecated, "
2022 2022 "use 'orphan()'")
2023 2023 repo.ui.deprecwarn(msg, '4.4')
2024 2024
2025 2025 return orphan(repo, subset, x)
2026 2026
2027 2027 @predicate('orphan()', safe=True)
2028 2028 def orphan(repo, subset, x):
2029 2029 """Non-obsolete changesets with obsolete ancestors. (EXPERIMENTAL)
2030 2030 """
2031 2031 # i18n: "orphan" is a keyword
2032 2032 getargs(x, 0, 0, _("orphan takes no arguments"))
2033 2033 orphan = obsmod.getrevs(repo, 'orphan')
2034 2034 return subset & orphan
2035 2035
2036 2036
2037 2037 @predicate('user(string)', safe=True, weight=10)
2038 2038 def user(repo, subset, x):
2039 2039 """User name contains string. The match is case-insensitive.
2040 2040
2041 2041 Pattern matching is supported for `string`. See
2042 2042 :hg:`help revisions.patterns`.
2043 2043 """
2044 2044 return author(repo, subset, x)
2045 2045
2046 2046 @predicate('wdir()', safe=True, weight=0)
2047 2047 def wdir(repo, subset, x):
2048 2048 """Working directory. (EXPERIMENTAL)"""
2049 2049 # i18n: "wdir" is a keyword
2050 2050 getargs(x, 0, 0, _("wdir takes no arguments"))
2051 2051 if node.wdirrev in subset or isinstance(subset, fullreposet):
2052 2052 return baseset([node.wdirrev])
2053 2053 return baseset()
2054 2054
2055 2055 def _orderedlist(repo, subset, x):
2056 2056 s = getstring(x, "internal error")
2057 2057 if not s:
2058 2058 return baseset()
2059 2059 # remove duplicates here. it's difficult for caller to deduplicate sets
2060 2060 # because different symbols can point to the same rev.
2061 2061 cl = repo.changelog
2062 2062 ls = []
2063 2063 seen = set()
2064 2064 for t in s.split('\0'):
2065 2065 try:
2066 2066 # fast path for integer revision
2067 2067 r = int(t)
2068 2068 if str(r) != t or r not in cl:
2069 2069 raise ValueError
2070 2070 revs = [r]
2071 2071 except ValueError:
2072 2072 revs = stringset(repo, subset, t, defineorder)
2073 2073
2074 2074 for r in revs:
2075 2075 if r in seen:
2076 2076 continue
2077 2077 if (r in subset
2078 2078 or r == node.nullrev and isinstance(subset, fullreposet)):
2079 2079 ls.append(r)
2080 2080 seen.add(r)
2081 2081 return baseset(ls)
2082 2082
2083 2083 # for internal use
2084 2084 @predicate('_list', safe=True, takeorder=True)
2085 2085 def _list(repo, subset, x, order):
2086 2086 if order == followorder:
2087 2087 # slow path to take the subset order
2088 2088 return subset & _orderedlist(repo, fullreposet(repo), x)
2089 2089 else:
2090 2090 return _orderedlist(repo, subset, x)
2091 2091
2092 2092 def _orderedintlist(repo, subset, x):
2093 2093 s = getstring(x, "internal error")
2094 2094 if not s:
2095 2095 return baseset()
2096 2096 ls = [int(r) for r in s.split('\0')]
2097 2097 s = subset
2098 2098 return baseset([r for r in ls if r in s])
2099 2099
2100 2100 # for internal use
2101 2101 @predicate('_intlist', safe=True, takeorder=True, weight=0)
2102 2102 def _intlist(repo, subset, x, order):
2103 2103 if order == followorder:
2104 2104 # slow path to take the subset order
2105 2105 return subset & _orderedintlist(repo, fullreposet(repo), x)
2106 2106 else:
2107 2107 return _orderedintlist(repo, subset, x)
2108 2108
2109 2109 def _orderedhexlist(repo, subset, x):
2110 2110 s = getstring(x, "internal error")
2111 2111 if not s:
2112 2112 return baseset()
2113 2113 cl = repo.changelog
2114 2114 ls = [cl.rev(node.bin(r)) for r in s.split('\0')]
2115 2115 s = subset
2116 2116 return baseset([r for r in ls if r in s])
2117 2117
2118 2118 # for internal use
2119 2119 @predicate('_hexlist', safe=True, takeorder=True)
2120 2120 def _hexlist(repo, subset, x, order):
2121 2121 if order == followorder:
2122 2122 # slow path to take the subset order
2123 2123 return subset & _orderedhexlist(repo, fullreposet(repo), x)
2124 2124 else:
2125 2125 return _orderedhexlist(repo, subset, x)
2126 2126
2127 2127 methods = {
2128 2128 "range": rangeset,
2129 2129 "rangeall": rangeall,
2130 2130 "rangepre": rangepre,
2131 2131 "rangepost": rangepost,
2132 2132 "dagrange": dagrange,
2133 2133 "string": stringset,
2134 2134 "symbol": stringset,
2135 2135 "and": andset,
2136 2136 "andsmally": andsmallyset,
2137 2137 "or": orset,
2138 2138 "not": notset,
2139 2139 "difference": differenceset,
2140 2140 "relation": relationset,
2141 2141 "relsubscript": relsubscriptset,
2142 2142 "subscript": subscriptset,
2143 2143 "list": listset,
2144 2144 "keyvalue": keyvaluepair,
2145 2145 "func": func,
2146 2146 "ancestor": ancestorspec,
2147 2147 "parent": parentspec,
2148 2148 "parentpost": parentpost,
2149 2149 }
2150 2150
2151 2151 def posttreebuilthook(tree, repo):
2152 2152 # hook for extensions to execute code on the optimized tree
2153 2153 pass
2154 2154
2155 2155 def match(ui, spec, repo=None):
2156 2156 """Create a matcher for a single revision spec"""
2157 2157 return matchany(ui, [spec], repo=repo)
2158 2158
2159 2159 def matchany(ui, specs, repo=None, localalias=None):
2160 2160 """Create a matcher that will include any revisions matching one of the
2161 2161 given specs
2162 2162
2163 2163 If localalias is not None, it is a dict {name: definitionstring}. It takes
2164 2164 precedence over [revsetalias] config section.
2165 2165 """
2166 2166 if not specs:
2167 2167 def mfunc(repo, subset=None):
2168 2168 return baseset()
2169 2169 return mfunc
2170 2170 if not all(specs):
2171 2171 raise error.ParseError(_("empty query"))
2172 2172 lookup = None
2173 2173 if repo:
2174 2174 lookup = repo.__contains__
2175 2175 if len(specs) == 1:
2176 2176 tree = revsetlang.parse(specs[0], lookup)
2177 2177 else:
2178 2178 tree = ('or',
2179 2179 ('list',) + tuple(revsetlang.parse(s, lookup) for s in specs))
2180 2180
2181 2181 aliases = []
2182 2182 warn = None
2183 2183 if ui:
2184 2184 aliases.extend(ui.configitems('revsetalias'))
2185 2185 warn = ui.warn
2186 2186 if localalias:
2187 2187 aliases.extend(localalias.items())
2188 2188 if aliases:
2189 2189 tree = revsetlang.expandaliases(tree, aliases, warn=warn)
2190 2190 tree = revsetlang.foldconcat(tree)
2191 2191 tree = revsetlang.analyze(tree)
2192 2192 tree = revsetlang.optimize(tree)
2193 2193 posttreebuilthook(tree, repo)
2194 2194 return makematcher(tree)
2195 2195
2196 2196 def makematcher(tree):
2197 2197 """Create a matcher from an evaluatable tree"""
2198 2198 def mfunc(repo, subset=None, order=None):
2199 2199 if order is None:
2200 2200 if subset is None:
2201 2201 order = defineorder # 'x'
2202 2202 else:
2203 2203 order = followorder # 'subset & x'
2204 2204 if subset is None:
2205 2205 subset = fullreposet(repo)
2206 2206 return getset(repo, subset, tree, order)
2207 2207 return mfunc
2208 2208
2209 2209 def loadpredicate(ui, extname, registrarobj):
2210 2210 """Load revset predicates from specified registrarobj
2211 2211 """
2212 2212 for name, func in registrarobj._table.iteritems():
2213 2213 symbols[name] = func
2214 2214 if func._safe:
2215 2215 safesymbols.add(name)
2216 2216
2217 2217 # load built-in predicates explicitly to setup safesymbols
2218 2218 loadpredicate(None, None, predicate)
2219 2219
2220 2220 # tell hggettext to extract docstrings from these functions:
2221 2221 i18nfunctions = symbols.values()
@@ -1,2466 +1,2479
1 1 Log on empty repository: checking consistency
2 2
3 3 $ hg init empty
4 4 $ cd empty
5 5 $ hg log
6 6 $ hg log -r 1
7 7 abort: unknown revision '1'!
8 8 [255]
9 9 $ hg log -r -1:0
10 10 abort: unknown revision '-1'!
11 11 [255]
12 12 $ hg log -r 'branch(name)'
13 13 abort: unknown revision 'name'!
14 14 [255]
15 15 $ hg log -r null -q
16 16 -1:000000000000
17 17
18 18 $ cd ..
19 19
20 20 The g is crafted to have 2 filelog topological heads in a linear
21 21 changeset graph
22 22
23 23 $ hg init a
24 24 $ cd a
25 25 $ echo a > a
26 26 $ echo f > f
27 27 $ hg ci -Ama -d '1 0'
28 28 adding a
29 29 adding f
30 30
31 31 $ hg cp a b
32 32 $ hg cp f g
33 33 $ hg ci -mb -d '2 0'
34 34
35 35 $ mkdir dir
36 36 $ hg mv b dir
37 37 $ echo g >> g
38 38 $ echo f >> f
39 39 $ hg ci -mc -d '3 0'
40 40
41 41 $ hg mv a b
42 42 $ hg cp -f f g
43 43 $ echo a > d
44 44 $ hg add d
45 45 $ hg ci -md -d '4 0'
46 46
47 47 $ hg mv dir/b e
48 48 $ hg ci -me -d '5 0'
49 49
50 50 Make sure largefiles doesn't interfere with logging a regular file
51 51 $ hg --debug log a -T '{rev}: {desc}\n' --config extensions.largefiles=
52 52 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
53 53 updated patterns: .hglf/a, a
54 54 0: a
55 55 $ hg log a
56 56 changeset: 0:9161b9aeaf16
57 57 user: test
58 58 date: Thu Jan 01 00:00:01 1970 +0000
59 59 summary: a
60 60
61 61 $ hg log glob:a*
62 62 changeset: 3:2ca5ba701980
63 63 user: test
64 64 date: Thu Jan 01 00:00:04 1970 +0000
65 65 summary: d
66 66
67 67 changeset: 0:9161b9aeaf16
68 68 user: test
69 69 date: Thu Jan 01 00:00:01 1970 +0000
70 70 summary: a
71 71
72 72 $ hg --debug log glob:a* -T '{rev}: {desc}\n' --config extensions.largefiles=
73 73 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
74 74 updated patterns: glob:.hglf/a*, glob:a*
75 75 3: d
76 76 0: a
77 77
78 78 log on directory
79 79
80 80 $ hg log dir
81 81 changeset: 4:7e4639b4691b
82 82 tag: tip
83 83 user: test
84 84 date: Thu Jan 01 00:00:05 1970 +0000
85 85 summary: e
86 86
87 87 changeset: 2:f8954cd4dc1f
88 88 user: test
89 89 date: Thu Jan 01 00:00:03 1970 +0000
90 90 summary: c
91 91
92 92 $ hg log somethingthatdoesntexist dir
93 93 changeset: 4:7e4639b4691b
94 94 tag: tip
95 95 user: test
96 96 date: Thu Jan 01 00:00:05 1970 +0000
97 97 summary: e
98 98
99 99 changeset: 2:f8954cd4dc1f
100 100 user: test
101 101 date: Thu Jan 01 00:00:03 1970 +0000
102 102 summary: c
103 103
104 104
105 105 -f, non-existent directory
106 106
107 107 $ hg log -f dir
108 108 abort: cannot follow file not in parent revision: "dir"
109 109 [255]
110 110
111 111 -f, directory
112 112
113 113 $ hg up -q 3
114 114 $ hg log -f dir
115 115 changeset: 2:f8954cd4dc1f
116 116 user: test
117 117 date: Thu Jan 01 00:00:03 1970 +0000
118 118 summary: c
119 119
120 120 -f, directory with --patch
121 121
122 122 $ hg log -f dir -p
123 123 changeset: 2:f8954cd4dc1f
124 124 user: test
125 125 date: Thu Jan 01 00:00:03 1970 +0000
126 126 summary: c
127 127
128 128 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
129 129 --- /dev/null* (glob)
130 130 +++ b/dir/b* (glob)
131 131 @@ -0,0 +1,1 @@
132 132 +a
133 133
134 134
135 135 -f, pattern
136 136
137 137 $ hg log -f -I 'dir**' -p
138 138 changeset: 2:f8954cd4dc1f
139 139 user: test
140 140 date: Thu Jan 01 00:00:03 1970 +0000
141 141 summary: c
142 142
143 143 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
144 144 --- /dev/null* (glob)
145 145 +++ b/dir/b* (glob)
146 146 @@ -0,0 +1,1 @@
147 147 +a
148 148
149 149 $ hg up -q 4
150 150
151 151 -f, a wrong style
152 152
153 153 $ hg log -f -l1 --style something
154 154 abort: style 'something' not found
155 155 (available styles: bisect, changelog, compact, default, phases, show, status, xml)
156 156 [255]
157 157
158 158 -f, phases style
159 159
160 160
161 161 $ hg log -f -l1 --style phases
162 162 changeset: 4:7e4639b4691b
163 163 tag: tip
164 164 phase: draft
165 165 user: test
166 166 date: Thu Jan 01 00:00:05 1970 +0000
167 167 summary: e
168 168
169 169
170 170 $ hg log -f -l1 --style phases -q
171 171 4:7e4639b4691b
172 172
173 173 -f, but no args
174 174
175 175 $ hg log -f
176 176 changeset: 4:7e4639b4691b
177 177 tag: tip
178 178 user: test
179 179 date: Thu Jan 01 00:00:05 1970 +0000
180 180 summary: e
181 181
182 182 changeset: 3:2ca5ba701980
183 183 user: test
184 184 date: Thu Jan 01 00:00:04 1970 +0000
185 185 summary: d
186 186
187 187 changeset: 2:f8954cd4dc1f
188 188 user: test
189 189 date: Thu Jan 01 00:00:03 1970 +0000
190 190 summary: c
191 191
192 192 changeset: 1:d89b0a12d229
193 193 user: test
194 194 date: Thu Jan 01 00:00:02 1970 +0000
195 195 summary: b
196 196
197 197 changeset: 0:9161b9aeaf16
198 198 user: test
199 199 date: Thu Jan 01 00:00:01 1970 +0000
200 200 summary: a
201 201
202 202
203 203 one rename
204 204
205 205 $ hg up -q 2
206 206 $ hg log -vf a
207 207 changeset: 0:9161b9aeaf16
208 208 user: test
209 209 date: Thu Jan 01 00:00:01 1970 +0000
210 210 files: a f
211 211 description:
212 212 a
213 213
214 214
215 215
216 216 many renames
217 217
218 218 $ hg up -q tip
219 219 $ hg log -vf e
220 220 changeset: 4:7e4639b4691b
221 221 tag: tip
222 222 user: test
223 223 date: Thu Jan 01 00:00:05 1970 +0000
224 224 files: dir/b e
225 225 description:
226 226 e
227 227
228 228
229 229 changeset: 2:f8954cd4dc1f
230 230 user: test
231 231 date: Thu Jan 01 00:00:03 1970 +0000
232 232 files: b dir/b f g
233 233 description:
234 234 c
235 235
236 236
237 237 changeset: 1:d89b0a12d229
238 238 user: test
239 239 date: Thu Jan 01 00:00:02 1970 +0000
240 240 files: b g
241 241 description:
242 242 b
243 243
244 244
245 245 changeset: 0:9161b9aeaf16
246 246 user: test
247 247 date: Thu Jan 01 00:00:01 1970 +0000
248 248 files: a f
249 249 description:
250 250 a
251 251
252 252
253 253
254 254
255 255 log -pf dir/b
256 256
257 257 $ hg up -q 3
258 258 $ hg log -pf dir/b
259 259 changeset: 2:f8954cd4dc1f
260 260 user: test
261 261 date: Thu Jan 01 00:00:03 1970 +0000
262 262 summary: c
263 263
264 264 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
265 265 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
266 266 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
267 267 @@ -0,0 +1,1 @@
268 268 +a
269 269
270 270 changeset: 1:d89b0a12d229
271 271 user: test
272 272 date: Thu Jan 01 00:00:02 1970 +0000
273 273 summary: b
274 274
275 275 diff -r 9161b9aeaf16 -r d89b0a12d229 b
276 276 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
277 277 +++ b/b Thu Jan 01 00:00:02 1970 +0000
278 278 @@ -0,0 +1,1 @@
279 279 +a
280 280
281 281 changeset: 0:9161b9aeaf16
282 282 user: test
283 283 date: Thu Jan 01 00:00:01 1970 +0000
284 284 summary: a
285 285
286 286 diff -r 000000000000 -r 9161b9aeaf16 a
287 287 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
288 288 +++ b/a Thu Jan 01 00:00:01 1970 +0000
289 289 @@ -0,0 +1,1 @@
290 290 +a
291 291
292 292
293 293 log -pf b inside dir
294 294
295 295 $ hg --cwd=dir log -pf b
296 296 changeset: 2:f8954cd4dc1f
297 297 user: test
298 298 date: Thu Jan 01 00:00:03 1970 +0000
299 299 summary: c
300 300
301 301 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
302 302 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
303 303 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
304 304 @@ -0,0 +1,1 @@
305 305 +a
306 306
307 307 changeset: 1:d89b0a12d229
308 308 user: test
309 309 date: Thu Jan 01 00:00:02 1970 +0000
310 310 summary: b
311 311
312 312 diff -r 9161b9aeaf16 -r d89b0a12d229 b
313 313 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
314 314 +++ b/b Thu Jan 01 00:00:02 1970 +0000
315 315 @@ -0,0 +1,1 @@
316 316 +a
317 317
318 318 changeset: 0:9161b9aeaf16
319 319 user: test
320 320 date: Thu Jan 01 00:00:01 1970 +0000
321 321 summary: a
322 322
323 323 diff -r 000000000000 -r 9161b9aeaf16 a
324 324 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
325 325 +++ b/a Thu Jan 01 00:00:01 1970 +0000
326 326 @@ -0,0 +1,1 @@
327 327 +a
328 328
329 329
330 330 log -pf, but no args
331 331
332 332 $ hg log -pf
333 333 changeset: 3:2ca5ba701980
334 334 user: test
335 335 date: Thu Jan 01 00:00:04 1970 +0000
336 336 summary: d
337 337
338 338 diff -r f8954cd4dc1f -r 2ca5ba701980 a
339 339 --- a/a Thu Jan 01 00:00:03 1970 +0000
340 340 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
341 341 @@ -1,1 +0,0 @@
342 342 -a
343 343 diff -r f8954cd4dc1f -r 2ca5ba701980 b
344 344 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
345 345 +++ b/b Thu Jan 01 00:00:04 1970 +0000
346 346 @@ -0,0 +1,1 @@
347 347 +a
348 348 diff -r f8954cd4dc1f -r 2ca5ba701980 d
349 349 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
350 350 +++ b/d Thu Jan 01 00:00:04 1970 +0000
351 351 @@ -0,0 +1,1 @@
352 352 +a
353 353 diff -r f8954cd4dc1f -r 2ca5ba701980 g
354 354 --- a/g Thu Jan 01 00:00:03 1970 +0000
355 355 +++ b/g Thu Jan 01 00:00:04 1970 +0000
356 356 @@ -1,2 +1,2 @@
357 357 f
358 358 -g
359 359 +f
360 360
361 361 changeset: 2:f8954cd4dc1f
362 362 user: test
363 363 date: Thu Jan 01 00:00:03 1970 +0000
364 364 summary: c
365 365
366 366 diff -r d89b0a12d229 -r f8954cd4dc1f b
367 367 --- a/b Thu Jan 01 00:00:02 1970 +0000
368 368 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
369 369 @@ -1,1 +0,0 @@
370 370 -a
371 371 diff -r d89b0a12d229 -r f8954cd4dc1f dir/b
372 372 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
373 373 +++ b/dir/b Thu Jan 01 00:00:03 1970 +0000
374 374 @@ -0,0 +1,1 @@
375 375 +a
376 376 diff -r d89b0a12d229 -r f8954cd4dc1f f
377 377 --- a/f Thu Jan 01 00:00:02 1970 +0000
378 378 +++ b/f Thu Jan 01 00:00:03 1970 +0000
379 379 @@ -1,1 +1,2 @@
380 380 f
381 381 +f
382 382 diff -r d89b0a12d229 -r f8954cd4dc1f g
383 383 --- a/g Thu Jan 01 00:00:02 1970 +0000
384 384 +++ b/g Thu Jan 01 00:00:03 1970 +0000
385 385 @@ -1,1 +1,2 @@
386 386 f
387 387 +g
388 388
389 389 changeset: 1:d89b0a12d229
390 390 user: test
391 391 date: Thu Jan 01 00:00:02 1970 +0000
392 392 summary: b
393 393
394 394 diff -r 9161b9aeaf16 -r d89b0a12d229 b
395 395 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
396 396 +++ b/b Thu Jan 01 00:00:02 1970 +0000
397 397 @@ -0,0 +1,1 @@
398 398 +a
399 399 diff -r 9161b9aeaf16 -r d89b0a12d229 g
400 400 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
401 401 +++ b/g Thu Jan 01 00:00:02 1970 +0000
402 402 @@ -0,0 +1,1 @@
403 403 +f
404 404
405 405 changeset: 0:9161b9aeaf16
406 406 user: test
407 407 date: Thu Jan 01 00:00:01 1970 +0000
408 408 summary: a
409 409
410 410 diff -r 000000000000 -r 9161b9aeaf16 a
411 411 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
412 412 +++ b/a Thu Jan 01 00:00:01 1970 +0000
413 413 @@ -0,0 +1,1 @@
414 414 +a
415 415 diff -r 000000000000 -r 9161b9aeaf16 f
416 416 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
417 417 +++ b/f Thu Jan 01 00:00:01 1970 +0000
418 418 @@ -0,0 +1,1 @@
419 419 +f
420 420
421 421
422 422 log -vf dir/b
423 423
424 424 $ hg log -vf dir/b
425 425 changeset: 2:f8954cd4dc1f
426 426 user: test
427 427 date: Thu Jan 01 00:00:03 1970 +0000
428 428 files: b dir/b f g
429 429 description:
430 430 c
431 431
432 432
433 433 changeset: 1:d89b0a12d229
434 434 user: test
435 435 date: Thu Jan 01 00:00:02 1970 +0000
436 436 files: b g
437 437 description:
438 438 b
439 439
440 440
441 441 changeset: 0:9161b9aeaf16
442 442 user: test
443 443 date: Thu Jan 01 00:00:01 1970 +0000
444 444 files: a f
445 445 description:
446 446 a
447 447
448 448
449 449
450 450
451 451 -f and multiple filelog heads
452 452
453 453 $ hg up -q 2
454 454 $ hg log -f g --template '{rev}\n'
455 455 2
456 456 1
457 457 0
458 458 $ hg up -q tip
459 459 $ hg log -f g --template '{rev}\n'
460 460 3
461 461 2
462 462 0
463 463
464 464
465 465 log copies with --copies
466 466
467 467 $ hg log -vC --template '{rev} {file_copies}\n'
468 468 4 e (dir/b)
469 469 3 b (a)g (f)
470 470 2 dir/b (b)
471 471 1 b (a)g (f)
472 472 0
473 473
474 474 log copies switch without --copies, with old filecopy template
475 475
476 476 $ hg log -v --template '{rev} {file_copies_switch%filecopy}\n'
477 477 4
478 478 3
479 479 2
480 480 1
481 481 0
482 482
483 483 log copies switch with --copies
484 484
485 485 $ hg log -vC --template '{rev} {file_copies_switch}\n'
486 486 4 e (dir/b)
487 487 3 b (a)g (f)
488 488 2 dir/b (b)
489 489 1 b (a)g (f)
490 490 0
491 491
492 492
493 493 log copies with hardcoded style and with --style=default
494 494
495 495 $ hg log -vC -r4
496 496 changeset: 4:7e4639b4691b
497 497 tag: tip
498 498 user: test
499 499 date: Thu Jan 01 00:00:05 1970 +0000
500 500 files: dir/b e
501 501 copies: e (dir/b)
502 502 description:
503 503 e
504 504
505 505
506 506 $ hg log -vC -r4 --style=default
507 507 changeset: 4:7e4639b4691b
508 508 tag: tip
509 509 user: test
510 510 date: Thu Jan 01 00:00:05 1970 +0000
511 511 files: dir/b e
512 512 copies: e (dir/b)
513 513 description:
514 514 e
515 515
516 516
517 517 $ hg log -vC -r4 -Tjson
518 518 [
519 519 {
520 520 "rev": 4,
521 521 "node": "7e4639b4691b9f84b81036a8d4fb218ce3c5e3a3",
522 522 "branch": "default",
523 523 "phase": "draft",
524 524 "user": "test",
525 525 "date": [5, 0],
526 526 "desc": "e",
527 527 "bookmarks": [],
528 528 "tags": ["tip"],
529 529 "parents": ["2ca5ba7019804f1f597249caddf22a64d34df0ba"],
530 530 "files": ["dir/b", "e"],
531 531 "copies": {"e": "dir/b"}
532 532 }
533 533 ]
534 534
535 535 log copies, non-linear manifest
536 536
537 537 $ hg up -C 3
538 538 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
539 539 $ hg mv dir/b e
540 540 $ echo foo > foo
541 541 $ hg ci -Ame2 -d '6 0'
542 542 adding foo
543 543 created new head
544 544 $ hg log -v --template '{rev} {file_copies}\n' -r 5
545 545 5 e (dir/b)
546 546
547 547
548 548 log copies, execute bit set
549 549
550 550 #if execbit
551 551 $ chmod +x e
552 552 $ hg ci -me3 -d '7 0'
553 553 $ hg log -v --template '{rev} {file_copies}\n' -r 6
554 554 6
555 555 #endif
556 556
557 557
558 558 log -p d
559 559
560 560 $ hg log -pv d
561 561 changeset: 3:2ca5ba701980
562 562 user: test
563 563 date: Thu Jan 01 00:00:04 1970 +0000
564 564 files: a b d g
565 565 description:
566 566 d
567 567
568 568
569 569 diff -r f8954cd4dc1f -r 2ca5ba701980 d
570 570 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
571 571 +++ b/d Thu Jan 01 00:00:04 1970 +0000
572 572 @@ -0,0 +1,1 @@
573 573 +a
574 574
575 575
576 576
577 577 log --removed file
578 578
579 579 $ hg log --removed -v a
580 580 changeset: 3:2ca5ba701980
581 581 user: test
582 582 date: Thu Jan 01 00:00:04 1970 +0000
583 583 files: a b d g
584 584 description:
585 585 d
586 586
587 587
588 588 changeset: 0:9161b9aeaf16
589 589 user: test
590 590 date: Thu Jan 01 00:00:01 1970 +0000
591 591 files: a f
592 592 description:
593 593 a
594 594
595 595
596 596
597 597 log --removed revrange file
598 598
599 599 $ hg log --removed -v -r0:2 a
600 600 changeset: 0:9161b9aeaf16
601 601 user: test
602 602 date: Thu Jan 01 00:00:01 1970 +0000
603 603 files: a f
604 604 description:
605 605 a
606 606
607 607
608 608 $ cd ..
609 609
610 610 log --follow tests
611 611
612 612 $ hg init follow
613 613 $ cd follow
614 614
615 615 $ echo base > base
616 616 $ hg ci -Ambase -d '1 0'
617 617 adding base
618 618
619 619 $ echo r1 >> base
620 620 $ hg ci -Amr1 -d '1 0'
621 621 $ echo r2 >> base
622 622 $ hg ci -Amr2 -d '1 0'
623 623
624 624 $ hg up -C 1
625 625 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
626 626 $ echo b1 > b1
627 627
628 628 log -r "follow('set:clean()')"
629 629
630 630 $ hg log -r "follow('set:clean()')"
631 631 changeset: 0:67e992f2c4f3
632 632 user: test
633 633 date: Thu Jan 01 00:00:01 1970 +0000
634 634 summary: base
635 635
636 636 changeset: 1:3d5bf5654eda
637 637 user: test
638 638 date: Thu Jan 01 00:00:01 1970 +0000
639 639 summary: r1
640 640
641 641
642 642 $ hg ci -Amb1 -d '1 0'
643 643 adding b1
644 644 created new head
645 645
646 646
647 647 log -f
648 648
649 649 $ hg log -f
650 650 changeset: 3:e62f78d544b4
651 651 tag: tip
652 652 parent: 1:3d5bf5654eda
653 653 user: test
654 654 date: Thu Jan 01 00:00:01 1970 +0000
655 655 summary: b1
656 656
657 657 changeset: 1:3d5bf5654eda
658 658 user: test
659 659 date: Thu Jan 01 00:00:01 1970 +0000
660 660 summary: r1
661 661
662 662 changeset: 0:67e992f2c4f3
663 663 user: test
664 664 date: Thu Jan 01 00:00:01 1970 +0000
665 665 summary: base
666 666
667 667
668 668 log -r follow('glob:b*')
669 669
670 670 $ hg log -r "follow('glob:b*')"
671 671 changeset: 0:67e992f2c4f3
672 672 user: test
673 673 date: Thu Jan 01 00:00:01 1970 +0000
674 674 summary: base
675 675
676 676 changeset: 1:3d5bf5654eda
677 677 user: test
678 678 date: Thu Jan 01 00:00:01 1970 +0000
679 679 summary: r1
680 680
681 681 changeset: 3:e62f78d544b4
682 682 tag: tip
683 683 parent: 1:3d5bf5654eda
684 684 user: test
685 685 date: Thu Jan 01 00:00:01 1970 +0000
686 686 summary: b1
687 687
688 688 log -f -r '1 + 4'
689 689
690 690 $ hg up -C 0
691 691 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
692 692 $ echo b2 > b2
693 693 $ hg ci -Amb2 -d '1 0'
694 694 adding b2
695 695 created new head
696 696 $ hg log -f -r '1 + 4'
697 697 changeset: 4:ddb82e70d1a1
698 698 tag: tip
699 699 parent: 0:67e992f2c4f3
700 700 user: test
701 701 date: Thu Jan 01 00:00:01 1970 +0000
702 702 summary: b2
703 703
704 704 changeset: 1:3d5bf5654eda
705 705 user: test
706 706 date: Thu Jan 01 00:00:01 1970 +0000
707 707 summary: r1
708 708
709 709 changeset: 0:67e992f2c4f3
710 710 user: test
711 711 date: Thu Jan 01 00:00:01 1970 +0000
712 712 summary: base
713 713
714 714 log -r "follow('set:grep(b2)')"
715 715
716 716 $ hg log -r "follow('set:grep(b2)')"
717 717 changeset: 4:ddb82e70d1a1
718 718 tag: tip
719 719 parent: 0:67e992f2c4f3
720 720 user: test
721 721 date: Thu Jan 01 00:00:01 1970 +0000
722 722 summary: b2
723 723
724 724 log -r "follow('set:grep(b2)', 4)"
725 725
726 726 $ hg up -qC 0
727 727 $ hg log -r "follow('set:grep(b2)', 4)"
728 728 changeset: 4:ddb82e70d1a1
729 729 tag: tip
730 730 parent: 0:67e992f2c4f3
731 731 user: test
732 732 date: Thu Jan 01 00:00:01 1970 +0000
733 733 summary: b2
734 734
735
736 follow files starting from multiple revisions:
737
738 $ hg log -T '{rev}: {files}\n' -r "follow('glob:b?', 2+3+4)"
739 3: b1
740 4: b2
741
742 follow files starting from empty revision:
743
744 $ hg log -T '{rev}: {files}\n' -r "follow('glob:*', .-.)"
745 abort: follow expected at least one starting revision!
746 [255]
747
735 748 $ hg up -qC 4
736 749
737 750 log -f -r null
738 751
739 752 $ hg log -f -r null
740 753 changeset: -1:000000000000
741 754 user:
742 755 date: Thu Jan 01 00:00:00 1970 +0000
743 756
744 757 $ hg log -f -r null -G
745 758 o changeset: -1:000000000000
746 759 user:
747 760 date: Thu Jan 01 00:00:00 1970 +0000
748 761
749 762
750 763
751 764 log -f with null parent
752 765
753 766 $ hg up -C null
754 767 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
755 768 $ hg log -f
756 769
757 770
758 771 log -r . with two parents
759 772
760 773 $ hg up -C 3
761 774 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
762 775 $ hg merge tip
763 776 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
764 777 (branch merge, don't forget to commit)
765 778 $ hg log -r .
766 779 changeset: 3:e62f78d544b4
767 780 parent: 1:3d5bf5654eda
768 781 user: test
769 782 date: Thu Jan 01 00:00:01 1970 +0000
770 783 summary: b1
771 784
772 785
773 786
774 787 log -r . with one parent
775 788
776 789 $ hg ci -mm12 -d '1 0'
777 790 $ hg log -r .
778 791 changeset: 5:302e9dd6890d
779 792 tag: tip
780 793 parent: 3:e62f78d544b4
781 794 parent: 4:ddb82e70d1a1
782 795 user: test
783 796 date: Thu Jan 01 00:00:01 1970 +0000
784 797 summary: m12
785 798
786 799
787 800 $ echo postm >> b1
788 801 $ hg ci -Amb1.1 -d'1 0'
789 802
790 803
791 804 log --follow-first
792 805
793 806 $ hg log --follow-first
794 807 changeset: 6:2404bbcab562
795 808 tag: tip
796 809 user: test
797 810 date: Thu Jan 01 00:00:01 1970 +0000
798 811 summary: b1.1
799 812
800 813 changeset: 5:302e9dd6890d
801 814 parent: 3:e62f78d544b4
802 815 parent: 4:ddb82e70d1a1
803 816 user: test
804 817 date: Thu Jan 01 00:00:01 1970 +0000
805 818 summary: m12
806 819
807 820 changeset: 3:e62f78d544b4
808 821 parent: 1:3d5bf5654eda
809 822 user: test
810 823 date: Thu Jan 01 00:00:01 1970 +0000
811 824 summary: b1
812 825
813 826 changeset: 1:3d5bf5654eda
814 827 user: test
815 828 date: Thu Jan 01 00:00:01 1970 +0000
816 829 summary: r1
817 830
818 831 changeset: 0:67e992f2c4f3
819 832 user: test
820 833 date: Thu Jan 01 00:00:01 1970 +0000
821 834 summary: base
822 835
823 836
824 837
825 838 log -P 2
826 839
827 840 $ hg log -P 2
828 841 changeset: 6:2404bbcab562
829 842 tag: tip
830 843 user: test
831 844 date: Thu Jan 01 00:00:01 1970 +0000
832 845 summary: b1.1
833 846
834 847 changeset: 5:302e9dd6890d
835 848 parent: 3:e62f78d544b4
836 849 parent: 4:ddb82e70d1a1
837 850 user: test
838 851 date: Thu Jan 01 00:00:01 1970 +0000
839 852 summary: m12
840 853
841 854 changeset: 4:ddb82e70d1a1
842 855 parent: 0:67e992f2c4f3
843 856 user: test
844 857 date: Thu Jan 01 00:00:01 1970 +0000
845 858 summary: b2
846 859
847 860 changeset: 3:e62f78d544b4
848 861 parent: 1:3d5bf5654eda
849 862 user: test
850 863 date: Thu Jan 01 00:00:01 1970 +0000
851 864 summary: b1
852 865
853 866
854 867
855 868 log -r tip -p --git
856 869
857 870 $ hg log -r tip -p --git
858 871 changeset: 6:2404bbcab562
859 872 tag: tip
860 873 user: test
861 874 date: Thu Jan 01 00:00:01 1970 +0000
862 875 summary: b1.1
863 876
864 877 diff --git a/b1 b/b1
865 878 --- a/b1
866 879 +++ b/b1
867 880 @@ -1,1 +1,2 @@
868 881 b1
869 882 +postm
870 883
871 884
872 885
873 886 log -r ""
874 887
875 888 $ hg log -r ''
876 889 hg: parse error: empty query
877 890 [255]
878 891
879 892 log -r <some unknown node id>
880 893
881 894 $ hg log -r 1000000000000000000000000000000000000000
882 895 abort: unknown revision '1000000000000000000000000000000000000000'!
883 896 [255]
884 897
885 898 log -k r1
886 899
887 900 $ hg log -k r1
888 901 changeset: 1:3d5bf5654eda
889 902 user: test
890 903 date: Thu Jan 01 00:00:01 1970 +0000
891 904 summary: r1
892 905
893 906 log -p -l2 --color=always
894 907
895 908 $ hg --config extensions.color= --config color.mode=ansi \
896 909 > log -p -l2 --color=always
897 910 \x1b[0;33mchangeset: 6:2404bbcab562\x1b[0m (esc)
898 911 tag: tip
899 912 user: test
900 913 date: Thu Jan 01 00:00:01 1970 +0000
901 914 summary: b1.1
902 915
903 916 \x1b[0;1mdiff -r 302e9dd6890d -r 2404bbcab562 b1\x1b[0m (esc)
904 917 \x1b[0;31;1m--- a/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
905 918 \x1b[0;32;1m+++ b/b1 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
906 919 \x1b[0;35m@@ -1,1 +1,2 @@\x1b[0m (esc)
907 920 b1
908 921 \x1b[0;32m+postm\x1b[0m (esc)
909 922
910 923 \x1b[0;33mchangeset: 5:302e9dd6890d\x1b[0m (esc)
911 924 parent: 3:e62f78d544b4
912 925 parent: 4:ddb82e70d1a1
913 926 user: test
914 927 date: Thu Jan 01 00:00:01 1970 +0000
915 928 summary: m12
916 929
917 930 \x1b[0;1mdiff -r e62f78d544b4 -r 302e9dd6890d b2\x1b[0m (esc)
918 931 \x1b[0;31;1m--- /dev/null Thu Jan 01 00:00:00 1970 +0000\x1b[0m (esc)
919 932 \x1b[0;32;1m+++ b/b2 Thu Jan 01 00:00:01 1970 +0000\x1b[0m (esc)
920 933 \x1b[0;35m@@ -0,0 +1,1 @@\x1b[0m (esc)
921 934 \x1b[0;32m+b2\x1b[0m (esc)
922 935
923 936
924 937
925 938 log -r tip --stat
926 939
927 940 $ hg log -r tip --stat
928 941 changeset: 6:2404bbcab562
929 942 tag: tip
930 943 user: test
931 944 date: Thu Jan 01 00:00:01 1970 +0000
932 945 summary: b1.1
933 946
934 947 b1 | 1 +
935 948 1 files changed, 1 insertions(+), 0 deletions(-)
936 949
937 950
938 951 $ cd ..
939 952
940 953 log --follow --patch FILE in repository where linkrev isn't trustworthy
941 954 (issue5376)
942 955
943 956 $ hg init follow-dup
944 957 $ cd follow-dup
945 958 $ cat <<EOF >> .hg/hgrc
946 959 > [ui]
947 960 > logtemplate = '=== {rev}: {desc}\n'
948 961 > [diff]
949 962 > nodates = True
950 963 > EOF
951 964 $ echo 0 >> a
952 965 $ hg ci -qAm 'a0'
953 966 $ echo 1 >> a
954 967 $ hg ci -m 'a1'
955 968 $ hg up -q 0
956 969 $ echo 1 >> a
957 970 $ touch b
958 971 $ hg ci -qAm 'a1 with b'
959 972 $ echo 3 >> a
960 973 $ hg ci -m 'a3'
961 974
962 975 fctx.rev() == 2, but fctx.linkrev() == 1
963 976
964 977 $ hg log -pf a
965 978 === 3: a3
966 979 diff -r 4ea02ba94d66 -r e7a6331a34f0 a
967 980 --- a/a
968 981 +++ b/a
969 982 @@ -1,2 +1,3 @@
970 983 0
971 984 1
972 985 +3
973 986
974 987 === 2: a1 with b
975 988 diff -r 49b5e81287e2 -r 4ea02ba94d66 a
976 989 --- a/a
977 990 +++ b/a
978 991 @@ -1,1 +1,2 @@
979 992 0
980 993 +1
981 994
982 995 === 0: a0
983 996 diff -r 000000000000 -r 49b5e81287e2 a
984 997 --- /dev/null
985 998 +++ b/a
986 999 @@ -0,0 +1,1 @@
987 1000 +0
988 1001
989 1002
990 1003 fctx.introrev() == 2, but fctx.linkrev() == 1
991 1004
992 1005 $ hg up -q 2
993 1006 $ hg log -pf a
994 1007 === 2: a1 with b
995 1008 diff -r 49b5e81287e2 -r 4ea02ba94d66 a
996 1009 --- a/a
997 1010 +++ b/a
998 1011 @@ -1,1 +1,2 @@
999 1012 0
1000 1013 +1
1001 1014
1002 1015 === 0: a0
1003 1016 diff -r 000000000000 -r 49b5e81287e2 a
1004 1017 --- /dev/null
1005 1018 +++ b/a
1006 1019 @@ -0,0 +1,1 @@
1007 1020 +0
1008 1021
1009 1022
1010 1023 $ cd ..
1011 1024
1012 1025 Multiple copy sources of a file:
1013 1026
1014 1027 $ hg init follow-multi
1015 1028 $ cd follow-multi
1016 1029 $ echo 0 >> a
1017 1030 $ hg ci -qAm 'a'
1018 1031 $ hg cp a b
1019 1032 $ hg ci -m 'a->b'
1020 1033 $ echo 2 >> a
1021 1034 $ hg ci -m 'a'
1022 1035 $ echo 3 >> b
1023 1036 $ hg ci -m 'b'
1024 1037 $ echo 4 >> a
1025 1038 $ echo 4 >> b
1026 1039 $ hg ci -m 'a,b'
1027 1040 $ echo 5 >> a
1028 1041 $ hg ci -m 'a0'
1029 1042 $ echo 6 >> b
1030 1043 $ hg ci -m 'b0'
1031 1044 $ hg up -q 4
1032 1045 $ echo 7 >> b
1033 1046 $ hg ci -m 'b1'
1034 1047 created new head
1035 1048 $ echo 8 >> a
1036 1049 $ hg ci -m 'a1'
1037 1050 $ hg rm a
1038 1051 $ hg mv b a
1039 1052 $ hg ci -m 'b1->a1'
1040 1053 $ hg merge -qt :local
1041 1054 $ hg ci -m '(a0,b1->a1)->a'
1042 1055
1043 1056 $ hg log -GT '{rev}: {desc}\n'
1044 1057 @ 10: (a0,b1->a1)->a
1045 1058 |\
1046 1059 | o 9: b1->a1
1047 1060 | |
1048 1061 | o 8: a1
1049 1062 | |
1050 1063 | o 7: b1
1051 1064 | |
1052 1065 o | 6: b0
1053 1066 | |
1054 1067 o | 5: a0
1055 1068 |/
1056 1069 o 4: a,b
1057 1070 |
1058 1071 o 3: b
1059 1072 |
1060 1073 o 2: a
1061 1074 |
1062 1075 o 1: a->b
1063 1076 |
1064 1077 o 0: a
1065 1078
1066 1079
1067 1080 since file 'a' has multiple copy sources at the revision 4, ancestors can't
1068 1081 be indexed solely by fctx.linkrev().
1069 1082
1070 1083 $ hg log -T '{rev}: {desc}\n' -f a
1071 1084 10: (a0,b1->a1)->a
1072 1085 9: b1->a1
1073 1086 7: b1
1074 1087 5: a0
1075 1088 4: a,b
1076 1089 3: b
1077 1090 2: a
1078 1091 1: a->b
1079 1092 0: a
1080 1093
1081 1094 $ cd ..
1082 1095
1083 1096 Test that log should respect the order of -rREV even if multiple OR conditions
1084 1097 are specified (issue5100):
1085 1098
1086 1099 $ hg init revorder
1087 1100 $ cd revorder
1088 1101
1089 1102 $ hg branch -q b0
1090 1103 $ echo 0 >> f0
1091 1104 $ hg ci -qAm k0 -u u0
1092 1105 $ hg branch -q b1
1093 1106 $ echo 1 >> f1
1094 1107 $ hg ci -qAm k1 -u u1
1095 1108 $ hg branch -q b2
1096 1109 $ echo 2 >> f2
1097 1110 $ hg ci -qAm k2 -u u2
1098 1111
1099 1112 $ hg update -q b2
1100 1113 $ echo 3 >> f2
1101 1114 $ hg ci -qAm k2 -u u2
1102 1115 $ hg update -q b1
1103 1116 $ echo 4 >> f1
1104 1117 $ hg ci -qAm k1 -u u1
1105 1118 $ hg update -q b0
1106 1119 $ echo 5 >> f0
1107 1120 $ hg ci -qAm k0 -u u0
1108 1121
1109 1122 summary of revisions:
1110 1123
1111 1124 $ hg log -G -T '{rev} {branch} {author} {desc} {files}\n'
1112 1125 @ 5 b0 u0 k0 f0
1113 1126 |
1114 1127 | o 4 b1 u1 k1 f1
1115 1128 | |
1116 1129 | | o 3 b2 u2 k2 f2
1117 1130 | | |
1118 1131 | | o 2 b2 u2 k2 f2
1119 1132 | |/
1120 1133 | o 1 b1 u1 k1 f1
1121 1134 |/
1122 1135 o 0 b0 u0 k0 f0
1123 1136
1124 1137
1125 1138 log -b BRANCH in ascending order:
1126 1139
1127 1140 $ hg log -r0:tip -T '{rev} {branch}\n' -b b0 -b b1
1128 1141 0 b0
1129 1142 1 b1
1130 1143 4 b1
1131 1144 5 b0
1132 1145 $ hg log -r0:tip -T '{rev} {branch}\n' -b b1 -b b0
1133 1146 0 b0
1134 1147 1 b1
1135 1148 4 b1
1136 1149 5 b0
1137 1150
1138 1151 log --only-branch BRANCH in descending order:
1139 1152
1140 1153 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b1 --only-branch b2
1141 1154 4 b1
1142 1155 3 b2
1143 1156 2 b2
1144 1157 1 b1
1145 1158 $ hg log -rtip:0 -T '{rev} {branch}\n' --only-branch b2 --only-branch b1
1146 1159 4 b1
1147 1160 3 b2
1148 1161 2 b2
1149 1162 1 b1
1150 1163
1151 1164 log -u USER in ascending order, against compound set:
1152 1165
1153 1166 $ hg log -r'::head()' -T '{rev} {author}\n' -u u0 -u u2
1154 1167 0 u0
1155 1168 2 u2
1156 1169 3 u2
1157 1170 5 u0
1158 1171 $ hg log -r'::head()' -T '{rev} {author}\n' -u u2 -u u0
1159 1172 0 u0
1160 1173 2 u2
1161 1174 3 u2
1162 1175 5 u0
1163 1176
1164 1177 log -k TEXT in descending order, against compound set:
1165 1178
1166 1179 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k0 -k k1 -k k2
1167 1180 5 k0
1168 1181 3 k2
1169 1182 2 k2
1170 1183 1 k1
1171 1184 0 k0
1172 1185 $ hg log -r'5 + reverse(::3)' -T '{rev} {desc}\n' -k k2 -k k1 -k k0
1173 1186 5 k0
1174 1187 3 k2
1175 1188 2 k2
1176 1189 1 k1
1177 1190 0 k0
1178 1191
1179 1192 log FILE in ascending order, against dagrange:
1180 1193
1181 1194 $ hg log -r1:: -T '{rev} {files}\n' f1 f2
1182 1195 1 f1
1183 1196 2 f2
1184 1197 3 f2
1185 1198 4 f1
1186 1199 $ hg log -r1:: -T '{rev} {files}\n' f2 f1
1187 1200 1 f1
1188 1201 2 f2
1189 1202 3 f2
1190 1203 4 f1
1191 1204
1192 1205 $ cd ..
1193 1206
1194 1207 User
1195 1208
1196 1209 $ hg init usertest
1197 1210 $ cd usertest
1198 1211
1199 1212 $ echo a > a
1200 1213 $ hg ci -A -m "a" -u "User One <user1@example.org>"
1201 1214 adding a
1202 1215 $ echo b > b
1203 1216 $ hg ci -A -m "b" -u "User Two <user2@example.org>"
1204 1217 adding b
1205 1218
1206 1219 $ hg log -u "User One <user1@example.org>"
1207 1220 changeset: 0:29a4c94f1924
1208 1221 user: User One <user1@example.org>
1209 1222 date: Thu Jan 01 00:00:00 1970 +0000
1210 1223 summary: a
1211 1224
1212 1225 $ hg log -u "user1" -u "user2"
1213 1226 changeset: 1:e834b5e69c0e
1214 1227 tag: tip
1215 1228 user: User Two <user2@example.org>
1216 1229 date: Thu Jan 01 00:00:00 1970 +0000
1217 1230 summary: b
1218 1231
1219 1232 changeset: 0:29a4c94f1924
1220 1233 user: User One <user1@example.org>
1221 1234 date: Thu Jan 01 00:00:00 1970 +0000
1222 1235 summary: a
1223 1236
1224 1237 $ hg log -u "user3"
1225 1238
1226 1239 $ cd ..
1227 1240
1228 1241 $ hg init branches
1229 1242 $ cd branches
1230 1243
1231 1244 $ echo a > a
1232 1245 $ hg ci -A -m "commit on default"
1233 1246 adding a
1234 1247 $ hg branch test
1235 1248 marked working directory as branch test
1236 1249 (branches are permanent and global, did you want a bookmark?)
1237 1250 $ echo b > b
1238 1251 $ hg ci -A -m "commit on test"
1239 1252 adding b
1240 1253
1241 1254 $ hg up default
1242 1255 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1243 1256 $ echo c > c
1244 1257 $ hg ci -A -m "commit on default"
1245 1258 adding c
1246 1259 $ hg up test
1247 1260 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1248 1261 $ echo c > c
1249 1262 $ hg ci -A -m "commit on test"
1250 1263 adding c
1251 1264
1252 1265
1253 1266 log -b default
1254 1267
1255 1268 $ hg log -b default
1256 1269 changeset: 2:c3a4f03cc9a7
1257 1270 parent: 0:24427303d56f
1258 1271 user: test
1259 1272 date: Thu Jan 01 00:00:00 1970 +0000
1260 1273 summary: commit on default
1261 1274
1262 1275 changeset: 0:24427303d56f
1263 1276 user: test
1264 1277 date: Thu Jan 01 00:00:00 1970 +0000
1265 1278 summary: commit on default
1266 1279
1267 1280
1268 1281
1269 1282 log -b test
1270 1283
1271 1284 $ hg log -b test
1272 1285 changeset: 3:f5d8de11c2e2
1273 1286 branch: test
1274 1287 tag: tip
1275 1288 parent: 1:d32277701ccb
1276 1289 user: test
1277 1290 date: Thu Jan 01 00:00:00 1970 +0000
1278 1291 summary: commit on test
1279 1292
1280 1293 changeset: 1:d32277701ccb
1281 1294 branch: test
1282 1295 user: test
1283 1296 date: Thu Jan 01 00:00:00 1970 +0000
1284 1297 summary: commit on test
1285 1298
1286 1299
1287 1300
1288 1301 log -b dummy
1289 1302
1290 1303 $ hg log -b dummy
1291 1304 abort: unknown revision 'dummy'!
1292 1305 [255]
1293 1306
1294 1307
1295 1308 log -b .
1296 1309
1297 1310 $ hg log -b .
1298 1311 changeset: 3:f5d8de11c2e2
1299 1312 branch: test
1300 1313 tag: tip
1301 1314 parent: 1:d32277701ccb
1302 1315 user: test
1303 1316 date: Thu Jan 01 00:00:00 1970 +0000
1304 1317 summary: commit on test
1305 1318
1306 1319 changeset: 1:d32277701ccb
1307 1320 branch: test
1308 1321 user: test
1309 1322 date: Thu Jan 01 00:00:00 1970 +0000
1310 1323 summary: commit on test
1311 1324
1312 1325
1313 1326
1314 1327 log -b default -b test
1315 1328
1316 1329 $ hg log -b default -b test
1317 1330 changeset: 3:f5d8de11c2e2
1318 1331 branch: test
1319 1332 tag: tip
1320 1333 parent: 1:d32277701ccb
1321 1334 user: test
1322 1335 date: Thu Jan 01 00:00:00 1970 +0000
1323 1336 summary: commit on test
1324 1337
1325 1338 changeset: 2:c3a4f03cc9a7
1326 1339 parent: 0:24427303d56f
1327 1340 user: test
1328 1341 date: Thu Jan 01 00:00:00 1970 +0000
1329 1342 summary: commit on default
1330 1343
1331 1344 changeset: 1:d32277701ccb
1332 1345 branch: test
1333 1346 user: test
1334 1347 date: Thu Jan 01 00:00:00 1970 +0000
1335 1348 summary: commit on test
1336 1349
1337 1350 changeset: 0:24427303d56f
1338 1351 user: test
1339 1352 date: Thu Jan 01 00:00:00 1970 +0000
1340 1353 summary: commit on default
1341 1354
1342 1355
1343 1356
1344 1357 log -b default -b .
1345 1358
1346 1359 $ hg log -b default -b .
1347 1360 changeset: 3:f5d8de11c2e2
1348 1361 branch: test
1349 1362 tag: tip
1350 1363 parent: 1:d32277701ccb
1351 1364 user: test
1352 1365 date: Thu Jan 01 00:00:00 1970 +0000
1353 1366 summary: commit on test
1354 1367
1355 1368 changeset: 2:c3a4f03cc9a7
1356 1369 parent: 0:24427303d56f
1357 1370 user: test
1358 1371 date: Thu Jan 01 00:00:00 1970 +0000
1359 1372 summary: commit on default
1360 1373
1361 1374 changeset: 1:d32277701ccb
1362 1375 branch: test
1363 1376 user: test
1364 1377 date: Thu Jan 01 00:00:00 1970 +0000
1365 1378 summary: commit on test
1366 1379
1367 1380 changeset: 0:24427303d56f
1368 1381 user: test
1369 1382 date: Thu Jan 01 00:00:00 1970 +0000
1370 1383 summary: commit on default
1371 1384
1372 1385
1373 1386
1374 1387 log -b . -b test
1375 1388
1376 1389 $ hg log -b . -b test
1377 1390 changeset: 3:f5d8de11c2e2
1378 1391 branch: test
1379 1392 tag: tip
1380 1393 parent: 1:d32277701ccb
1381 1394 user: test
1382 1395 date: Thu Jan 01 00:00:00 1970 +0000
1383 1396 summary: commit on test
1384 1397
1385 1398 changeset: 1:d32277701ccb
1386 1399 branch: test
1387 1400 user: test
1388 1401 date: Thu Jan 01 00:00:00 1970 +0000
1389 1402 summary: commit on test
1390 1403
1391 1404
1392 1405
1393 1406 log -b 2
1394 1407
1395 1408 $ hg log -b 2
1396 1409 changeset: 2:c3a4f03cc9a7
1397 1410 parent: 0:24427303d56f
1398 1411 user: test
1399 1412 date: Thu Jan 01 00:00:00 1970 +0000
1400 1413 summary: commit on default
1401 1414
1402 1415 changeset: 0:24427303d56f
1403 1416 user: test
1404 1417 date: Thu Jan 01 00:00:00 1970 +0000
1405 1418 summary: commit on default
1406 1419
1407 1420 #if gettext
1408 1421
1409 1422 Test that all log names are translated (e.g. branches, bookmarks, tags):
1410 1423
1411 1424 $ hg bookmark babar -r tip
1412 1425
1413 1426 $ HGENCODING=UTF-8 LANGUAGE=de hg log -r tip
1414 1427 \xc3\x84nderung: 3:f5d8de11c2e2 (esc)
1415 1428 Zweig: test
1416 1429 Lesezeichen: babar
1417 1430 Marke: tip
1418 1431 Vorg\xc3\xa4nger: 1:d32277701ccb (esc)
1419 1432 Nutzer: test
1420 1433 Datum: Thu Jan 01 00:00:00 1970 +0000
1421 1434 Zusammenfassung: commit on test
1422 1435
1423 1436 $ hg bookmark -d babar
1424 1437
1425 1438 #endif
1426 1439
1427 1440 log -p --cwd dir (in subdir)
1428 1441
1429 1442 $ mkdir dir
1430 1443 $ hg log -p --cwd dir
1431 1444 changeset: 3:f5d8de11c2e2
1432 1445 branch: test
1433 1446 tag: tip
1434 1447 parent: 1:d32277701ccb
1435 1448 user: test
1436 1449 date: Thu Jan 01 00:00:00 1970 +0000
1437 1450 summary: commit on test
1438 1451
1439 1452 diff -r d32277701ccb -r f5d8de11c2e2 c
1440 1453 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1441 1454 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1442 1455 @@ -0,0 +1,1 @@
1443 1456 +c
1444 1457
1445 1458 changeset: 2:c3a4f03cc9a7
1446 1459 parent: 0:24427303d56f
1447 1460 user: test
1448 1461 date: Thu Jan 01 00:00:00 1970 +0000
1449 1462 summary: commit on default
1450 1463
1451 1464 diff -r 24427303d56f -r c3a4f03cc9a7 c
1452 1465 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1453 1466 +++ b/c Thu Jan 01 00:00:00 1970 +0000
1454 1467 @@ -0,0 +1,1 @@
1455 1468 +c
1456 1469
1457 1470 changeset: 1:d32277701ccb
1458 1471 branch: test
1459 1472 user: test
1460 1473 date: Thu Jan 01 00:00:00 1970 +0000
1461 1474 summary: commit on test
1462 1475
1463 1476 diff -r 24427303d56f -r d32277701ccb b
1464 1477 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1465 1478 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1466 1479 @@ -0,0 +1,1 @@
1467 1480 +b
1468 1481
1469 1482 changeset: 0:24427303d56f
1470 1483 user: test
1471 1484 date: Thu Jan 01 00:00:00 1970 +0000
1472 1485 summary: commit on default
1473 1486
1474 1487 diff -r 000000000000 -r 24427303d56f a
1475 1488 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1476 1489 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1477 1490 @@ -0,0 +1,1 @@
1478 1491 +a
1479 1492
1480 1493
1481 1494
1482 1495 log -p -R repo
1483 1496
1484 1497 $ cd dir
1485 1498 $ hg log -p -R .. ../a
1486 1499 changeset: 0:24427303d56f
1487 1500 user: test
1488 1501 date: Thu Jan 01 00:00:00 1970 +0000
1489 1502 summary: commit on default
1490 1503
1491 1504 diff -r 000000000000 -r 24427303d56f a
1492 1505 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1493 1506 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1494 1507 @@ -0,0 +1,1 @@
1495 1508 +a
1496 1509
1497 1510
1498 1511 $ cd ../..
1499 1512
1500 1513 $ hg init follow2
1501 1514 $ cd follow2
1502 1515
1503 1516 # Build the following history:
1504 1517 # tip - o - x - o - x - x
1505 1518 # \ /
1506 1519 # o - o - o - x
1507 1520 # \ /
1508 1521 # o
1509 1522 #
1510 1523 # Where "o" is a revision containing "foo" and
1511 1524 # "x" is a revision without "foo"
1512 1525
1513 1526 $ touch init
1514 1527 $ hg ci -A -m "init, unrelated"
1515 1528 adding init
1516 1529 $ echo 'foo' > init
1517 1530 $ hg ci -m "change, unrelated"
1518 1531 $ echo 'foo' > foo
1519 1532 $ hg ci -A -m "add unrelated old foo"
1520 1533 adding foo
1521 1534 $ hg rm foo
1522 1535 $ hg ci -m "delete foo, unrelated"
1523 1536 $ echo 'related' > foo
1524 1537 $ hg ci -A -m "add foo, related"
1525 1538 adding foo
1526 1539
1527 1540 $ hg up 0
1528 1541 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
1529 1542 $ touch branch
1530 1543 $ hg ci -A -m "first branch, unrelated"
1531 1544 adding branch
1532 1545 created new head
1533 1546 $ touch foo
1534 1547 $ hg ci -A -m "create foo, related"
1535 1548 adding foo
1536 1549 $ echo 'change' > foo
1537 1550 $ hg ci -m "change foo, related"
1538 1551
1539 1552 $ hg up 6
1540 1553 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1541 1554 $ echo 'change foo in branch' > foo
1542 1555 $ hg ci -m "change foo in branch, related"
1543 1556 created new head
1544 1557 $ hg merge 7
1545 1558 merging foo
1546 1559 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1547 1560 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
1548 1561 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1549 1562 [1]
1550 1563 $ echo 'merge 1' > foo
1551 1564 $ hg resolve -m foo
1552 1565 (no more unresolved files)
1553 1566 $ hg ci -m "First merge, related"
1554 1567
1555 1568 $ hg merge 4
1556 1569 merging foo
1557 1570 warning: conflicts while merging foo! (edit, then use 'hg resolve --mark')
1558 1571 1 files updated, 0 files merged, 0 files removed, 1 files unresolved
1559 1572 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
1560 1573 [1]
1561 1574 $ echo 'merge 2' > foo
1562 1575 $ hg resolve -m foo
1563 1576 (no more unresolved files)
1564 1577 $ hg ci -m "Last merge, related"
1565 1578
1566 1579 $ hg log --graph
1567 1580 @ changeset: 10:4dae8563d2c5
1568 1581 |\ tag: tip
1569 1582 | | parent: 9:7b35701b003e
1570 1583 | | parent: 4:88176d361b69
1571 1584 | | user: test
1572 1585 | | date: Thu Jan 01 00:00:00 1970 +0000
1573 1586 | | summary: Last merge, related
1574 1587 | |
1575 1588 | o changeset: 9:7b35701b003e
1576 1589 | |\ parent: 8:e5416ad8a855
1577 1590 | | | parent: 7:87fe3144dcfa
1578 1591 | | | user: test
1579 1592 | | | date: Thu Jan 01 00:00:00 1970 +0000
1580 1593 | | | summary: First merge, related
1581 1594 | | |
1582 1595 | | o changeset: 8:e5416ad8a855
1583 1596 | | | parent: 6:dc6c325fe5ee
1584 1597 | | | user: test
1585 1598 | | | date: Thu Jan 01 00:00:00 1970 +0000
1586 1599 | | | summary: change foo in branch, related
1587 1600 | | |
1588 1601 | o | changeset: 7:87fe3144dcfa
1589 1602 | |/ user: test
1590 1603 | | date: Thu Jan 01 00:00:00 1970 +0000
1591 1604 | | summary: change foo, related
1592 1605 | |
1593 1606 | o changeset: 6:dc6c325fe5ee
1594 1607 | | user: test
1595 1608 | | date: Thu Jan 01 00:00:00 1970 +0000
1596 1609 | | summary: create foo, related
1597 1610 | |
1598 1611 | o changeset: 5:73db34516eb9
1599 1612 | | parent: 0:e87515fd044a
1600 1613 | | user: test
1601 1614 | | date: Thu Jan 01 00:00:00 1970 +0000
1602 1615 | | summary: first branch, unrelated
1603 1616 | |
1604 1617 o | changeset: 4:88176d361b69
1605 1618 | | user: test
1606 1619 | | date: Thu Jan 01 00:00:00 1970 +0000
1607 1620 | | summary: add foo, related
1608 1621 | |
1609 1622 o | changeset: 3:dd78ae4afb56
1610 1623 | | user: test
1611 1624 | | date: Thu Jan 01 00:00:00 1970 +0000
1612 1625 | | summary: delete foo, unrelated
1613 1626 | |
1614 1627 o | changeset: 2:c4c64aedf0f7
1615 1628 | | user: test
1616 1629 | | date: Thu Jan 01 00:00:00 1970 +0000
1617 1630 | | summary: add unrelated old foo
1618 1631 | |
1619 1632 o | changeset: 1:e5faa7440653
1620 1633 |/ user: test
1621 1634 | date: Thu Jan 01 00:00:00 1970 +0000
1622 1635 | summary: change, unrelated
1623 1636 |
1624 1637 o changeset: 0:e87515fd044a
1625 1638 user: test
1626 1639 date: Thu Jan 01 00:00:00 1970 +0000
1627 1640 summary: init, unrelated
1628 1641
1629 1642
1630 1643 $ hg --traceback log -f foo
1631 1644 changeset: 10:4dae8563d2c5
1632 1645 tag: tip
1633 1646 parent: 9:7b35701b003e
1634 1647 parent: 4:88176d361b69
1635 1648 user: test
1636 1649 date: Thu Jan 01 00:00:00 1970 +0000
1637 1650 summary: Last merge, related
1638 1651
1639 1652 changeset: 9:7b35701b003e
1640 1653 parent: 8:e5416ad8a855
1641 1654 parent: 7:87fe3144dcfa
1642 1655 user: test
1643 1656 date: Thu Jan 01 00:00:00 1970 +0000
1644 1657 summary: First merge, related
1645 1658
1646 1659 changeset: 8:e5416ad8a855
1647 1660 parent: 6:dc6c325fe5ee
1648 1661 user: test
1649 1662 date: Thu Jan 01 00:00:00 1970 +0000
1650 1663 summary: change foo in branch, related
1651 1664
1652 1665 changeset: 7:87fe3144dcfa
1653 1666 user: test
1654 1667 date: Thu Jan 01 00:00:00 1970 +0000
1655 1668 summary: change foo, related
1656 1669
1657 1670 changeset: 6:dc6c325fe5ee
1658 1671 user: test
1659 1672 date: Thu Jan 01 00:00:00 1970 +0000
1660 1673 summary: create foo, related
1661 1674
1662 1675 changeset: 4:88176d361b69
1663 1676 user: test
1664 1677 date: Thu Jan 01 00:00:00 1970 +0000
1665 1678 summary: add foo, related
1666 1679
1667 1680
1668 1681 Also check when maxrev < lastrevfilelog
1669 1682
1670 1683 $ hg --traceback log -f -r4 foo
1671 1684 changeset: 4:88176d361b69
1672 1685 user: test
1673 1686 date: Thu Jan 01 00:00:00 1970 +0000
1674 1687 summary: add foo, related
1675 1688
1676 1689 changeset: 2:c4c64aedf0f7
1677 1690 user: test
1678 1691 date: Thu Jan 01 00:00:00 1970 +0000
1679 1692 summary: add unrelated old foo
1680 1693
1681 1694 $ cd ..
1682 1695
1683 1696 Issue2383: hg log showing _less_ differences than hg diff
1684 1697
1685 1698 $ hg init issue2383
1686 1699 $ cd issue2383
1687 1700
1688 1701 Create a test repo:
1689 1702
1690 1703 $ echo a > a
1691 1704 $ hg ci -Am0
1692 1705 adding a
1693 1706 $ echo b > b
1694 1707 $ hg ci -Am1
1695 1708 adding b
1696 1709 $ hg co 0
1697 1710 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1698 1711 $ echo b > a
1699 1712 $ hg ci -m2
1700 1713 created new head
1701 1714
1702 1715 Merge:
1703 1716
1704 1717 $ hg merge
1705 1718 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1706 1719 (branch merge, don't forget to commit)
1707 1720
1708 1721 Make sure there's a file listed in the merge to trigger the bug:
1709 1722
1710 1723 $ echo c > a
1711 1724 $ hg ci -m3
1712 1725
1713 1726 Two files shown here in diff:
1714 1727
1715 1728 $ hg diff --rev 2:3
1716 1729 diff -r b09be438c43a -r 8e07aafe1edc a
1717 1730 --- a/a Thu Jan 01 00:00:00 1970 +0000
1718 1731 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1719 1732 @@ -1,1 +1,1 @@
1720 1733 -b
1721 1734 +c
1722 1735 diff -r b09be438c43a -r 8e07aafe1edc b
1723 1736 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1724 1737 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1725 1738 @@ -0,0 +1,1 @@
1726 1739 +b
1727 1740
1728 1741 Diff here should be the same:
1729 1742
1730 1743 $ hg log -vpr 3
1731 1744 changeset: 3:8e07aafe1edc
1732 1745 tag: tip
1733 1746 parent: 2:b09be438c43a
1734 1747 parent: 1:925d80f479bb
1735 1748 user: test
1736 1749 date: Thu Jan 01 00:00:00 1970 +0000
1737 1750 files: a
1738 1751 description:
1739 1752 3
1740 1753
1741 1754
1742 1755 diff -r b09be438c43a -r 8e07aafe1edc a
1743 1756 --- a/a Thu Jan 01 00:00:00 1970 +0000
1744 1757 +++ b/a Thu Jan 01 00:00:00 1970 +0000
1745 1758 @@ -1,1 +1,1 @@
1746 1759 -b
1747 1760 +c
1748 1761 diff -r b09be438c43a -r 8e07aafe1edc b
1749 1762 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1750 1763 +++ b/b Thu Jan 01 00:00:00 1970 +0000
1751 1764 @@ -0,0 +1,1 @@
1752 1765 +b
1753 1766
1754 1767 $ cd ..
1755 1768
1756 1769 'hg log -r rev fn' when last(filelog(fn)) != rev
1757 1770
1758 1771 $ hg init simplelog
1759 1772 $ cd simplelog
1760 1773 $ echo f > a
1761 1774 $ hg ci -Am'a' -d '0 0'
1762 1775 adding a
1763 1776 $ echo f >> a
1764 1777 $ hg ci -Am'a bis' -d '1 0'
1765 1778
1766 1779 $ hg log -r0 a
1767 1780 changeset: 0:9f758d63dcde
1768 1781 user: test
1769 1782 date: Thu Jan 01 00:00:00 1970 +0000
1770 1783 summary: a
1771 1784
1772 1785 enable obsolete to test hidden feature
1773 1786
1774 1787 $ cat >> $HGRCPATH << EOF
1775 1788 > [experimental]
1776 1789 > evolution.createmarkers=True
1777 1790 > EOF
1778 1791
1779 1792 $ hg log --template='{rev}:{node}\n'
1780 1793 1:a765632148dc55d38c35c4f247c618701886cb2f
1781 1794 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1782 1795 $ hg debugobsolete a765632148dc55d38c35c4f247c618701886cb2f
1783 1796 obsoleted 1 changesets
1784 1797 $ hg up null -q
1785 1798 $ hg log --template='{rev}:{node}\n'
1786 1799 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1787 1800 $ hg log --template='{rev}:{node}\n' --hidden
1788 1801 1:a765632148dc55d38c35c4f247c618701886cb2f
1789 1802 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1790 1803 $ hg log -r a
1791 1804 abort: hidden revision 'a'!
1792 1805 (use --hidden to access hidden revisions)
1793 1806 [255]
1794 1807
1795 1808 test that parent prevent a changeset to be hidden
1796 1809
1797 1810 $ hg up 1 -q --hidden
1798 1811 $ hg log --template='{rev}:{node}\n'
1799 1812 1:a765632148dc55d38c35c4f247c618701886cb2f
1800 1813 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1801 1814
1802 1815 test that second parent prevent a changeset to be hidden too
1803 1816
1804 1817 $ hg debugsetparents 0 1 # nothing suitable to merge here
1805 1818 $ hg log --template='{rev}:{node}\n'
1806 1819 1:a765632148dc55d38c35c4f247c618701886cb2f
1807 1820 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1808 1821 $ hg debugsetparents 1
1809 1822 $ hg up -q null
1810 1823
1811 1824 bookmarks prevent a changeset being hidden
1812 1825
1813 1826 $ hg bookmark --hidden -r 1 X
1814 1827 $ hg log --template '{rev}:{node}\n'
1815 1828 1:a765632148dc55d38c35c4f247c618701886cb2f
1816 1829 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1817 1830 $ hg bookmark -d X
1818 1831
1819 1832 divergent bookmarks are not hidden
1820 1833
1821 1834 $ hg bookmark --hidden -r 1 X@foo
1822 1835 $ hg log --template '{rev}:{node}\n'
1823 1836 1:a765632148dc55d38c35c4f247c618701886cb2f
1824 1837 0:9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1825 1838
1826 1839 test hidden revision 0 (issue5385)
1827 1840
1828 1841 $ hg bookmark -d X@foo
1829 1842 $ hg up null -q
1830 1843 $ hg debugobsolete 9f758d63dcde62d547ebfb08e1e7ee96535f2b05
1831 1844 obsoleted 1 changesets
1832 1845 $ echo f > b
1833 1846 $ hg ci -Am'b' -d '2 0'
1834 1847 adding b
1835 1848 $ echo f >> b
1836 1849 $ hg ci -m'b bis' -d '3 0'
1837 1850 $ hg log -T'{rev}:{node}\n'
1838 1851 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
1839 1852 2:94375ec45bddd2a824535fc04855bd058c926ec0
1840 1853
1841 1854 $ hg log -T'{rev}:{node}\n' -r:
1842 1855 2:94375ec45bddd2a824535fc04855bd058c926ec0
1843 1856 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
1844 1857 $ hg log -T'{rev}:{node}\n' -r:tip
1845 1858 2:94375ec45bddd2a824535fc04855bd058c926ec0
1846 1859 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
1847 1860 $ hg log -T'{rev}:{node}\n' -r:0
1848 1861 abort: hidden revision '0'!
1849 1862 (use --hidden to access hidden revisions)
1850 1863 [255]
1851 1864 $ hg log -T'{rev}:{node}\n' -f
1852 1865 3:d7d28b288a6b83d5d2cf49f10c5974deed3a1d2e
1853 1866 2:94375ec45bddd2a824535fc04855bd058c926ec0
1854 1867
1855 1868 clear extensions configuration
1856 1869 $ echo '[extensions]' >> $HGRCPATH
1857 1870 $ echo "obs=!" >> $HGRCPATH
1858 1871 $ cd ..
1859 1872
1860 1873 test -u/-k for problematic encoding
1861 1874 # unicode: cp932:
1862 1875 # u30A2 0x83 0x41(= 'A')
1863 1876 # u30C2 0x83 0x61(= 'a')
1864 1877
1865 1878 $ hg init problematicencoding
1866 1879 $ cd problematicencoding
1867 1880
1868 1881 $ $PYTHON > setup.sh <<EOF
1869 1882 > print(u'''
1870 1883 > echo a > text
1871 1884 > hg add text
1872 1885 > hg --encoding utf-8 commit -u '\u30A2' -m none
1873 1886 > echo b > text
1874 1887 > hg --encoding utf-8 commit -u '\u30C2' -m none
1875 1888 > echo c > text
1876 1889 > hg --encoding utf-8 commit -u none -m '\u30A2'
1877 1890 > echo d > text
1878 1891 > hg --encoding utf-8 commit -u none -m '\u30C2'
1879 1892 > '''.encode('utf-8'))
1880 1893 > EOF
1881 1894 $ sh < setup.sh
1882 1895
1883 1896 test in problematic encoding
1884 1897 $ $PYTHON > test.sh <<EOF
1885 1898 > print(u'''
1886 1899 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30A2'
1887 1900 > echo ====
1888 1901 > hg --encoding cp932 log --template '{rev}\\n' -u '\u30C2'
1889 1902 > echo ====
1890 1903 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30A2'
1891 1904 > echo ====
1892 1905 > hg --encoding cp932 log --template '{rev}\\n' -k '\u30C2'
1893 1906 > '''.encode('cp932'))
1894 1907 > EOF
1895 1908 $ sh < test.sh
1896 1909 0
1897 1910 ====
1898 1911 1
1899 1912 ====
1900 1913 2
1901 1914 0
1902 1915 ====
1903 1916 3
1904 1917 1
1905 1918
1906 1919 $ cd ..
1907 1920
1908 1921 test hg log on non-existent files and on directories
1909 1922 $ hg init issue1340
1910 1923 $ cd issue1340
1911 1924 $ mkdir d1; mkdir D2; mkdir D3.i; mkdir d4.hg; mkdir d5.d; mkdir .d6
1912 1925 $ echo 1 > d1/f1
1913 1926 $ echo 1 > D2/f1
1914 1927 $ echo 1 > D3.i/f1
1915 1928 $ echo 1 > d4.hg/f1
1916 1929 $ echo 1 > d5.d/f1
1917 1930 $ echo 1 > .d6/f1
1918 1931 $ hg -q add .
1919 1932 $ hg commit -m "a bunch of weird directories"
1920 1933 $ hg log -l1 d1/f1 | grep changeset
1921 1934 changeset: 0:65624cd9070a
1922 1935 $ hg log -l1 f1
1923 1936 $ hg log -l1 . | grep changeset
1924 1937 changeset: 0:65624cd9070a
1925 1938 $ hg log -l1 ./ | grep changeset
1926 1939 changeset: 0:65624cd9070a
1927 1940 $ hg log -l1 d1 | grep changeset
1928 1941 changeset: 0:65624cd9070a
1929 1942 $ hg log -l1 D2 | grep changeset
1930 1943 changeset: 0:65624cd9070a
1931 1944 $ hg log -l1 D2/f1 | grep changeset
1932 1945 changeset: 0:65624cd9070a
1933 1946 $ hg log -l1 D3.i | grep changeset
1934 1947 changeset: 0:65624cd9070a
1935 1948 $ hg log -l1 D3.i/f1 | grep changeset
1936 1949 changeset: 0:65624cd9070a
1937 1950 $ hg log -l1 d4.hg | grep changeset
1938 1951 changeset: 0:65624cd9070a
1939 1952 $ hg log -l1 d4.hg/f1 | grep changeset
1940 1953 changeset: 0:65624cd9070a
1941 1954 $ hg log -l1 d5.d | grep changeset
1942 1955 changeset: 0:65624cd9070a
1943 1956 $ hg log -l1 d5.d/f1 | grep changeset
1944 1957 changeset: 0:65624cd9070a
1945 1958 $ hg log -l1 .d6 | grep changeset
1946 1959 changeset: 0:65624cd9070a
1947 1960 $ hg log -l1 .d6/f1 | grep changeset
1948 1961 changeset: 0:65624cd9070a
1949 1962
1950 1963 issue3772: hg log -r :null showing revision 0 as well
1951 1964
1952 1965 $ hg log -r :null
1953 1966 changeset: 0:65624cd9070a
1954 1967 tag: tip
1955 1968 user: test
1956 1969 date: Thu Jan 01 00:00:00 1970 +0000
1957 1970 summary: a bunch of weird directories
1958 1971
1959 1972 changeset: -1:000000000000
1960 1973 user:
1961 1974 date: Thu Jan 01 00:00:00 1970 +0000
1962 1975
1963 1976 $ hg log -r null:null
1964 1977 changeset: -1:000000000000
1965 1978 user:
1966 1979 date: Thu Jan 01 00:00:00 1970 +0000
1967 1980
1968 1981 working-directory revision requires special treatment
1969 1982
1970 1983 clean:
1971 1984
1972 1985 $ hg log -r 'wdir()' --debug
1973 1986 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
1974 1987 phase: draft
1975 1988 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
1976 1989 parent: -1:0000000000000000000000000000000000000000
1977 1990 user: test
1978 1991 date: [A-Za-z0-9:+ ]+ (re)
1979 1992 extra: branch=default
1980 1993
1981 1994 $ hg log -r 'wdir()' -p --stat
1982 1995 changeset: 2147483647:ffffffffffff
1983 1996 parent: 0:65624cd9070a
1984 1997 user: test
1985 1998 date: [A-Za-z0-9:+ ]+ (re)
1986 1999
1987 2000
1988 2001
1989 2002
1990 2003 dirty:
1991 2004
1992 2005 $ echo 2 >> d1/f1
1993 2006 $ echo 2 > d1/f2
1994 2007 $ hg add d1/f2
1995 2008 $ hg remove .d6/f1
1996 2009 $ hg status
1997 2010 M d1/f1
1998 2011 A d1/f2
1999 2012 R .d6/f1
2000 2013
2001 2014 $ hg log -r 'wdir()'
2002 2015 changeset: 2147483647:ffffffffffff
2003 2016 parent: 0:65624cd9070a
2004 2017 user: test
2005 2018 date: [A-Za-z0-9:+ ]+ (re)
2006 2019
2007 2020 $ hg log -r 'wdir()' -q
2008 2021 2147483647:ffffffffffff
2009 2022
2010 2023 $ hg log -r 'wdir()' --debug
2011 2024 changeset: 2147483647:ffffffffffffffffffffffffffffffffffffffff
2012 2025 phase: draft
2013 2026 parent: 0:65624cd9070a035fa7191a54f2b8af39f16b0c08
2014 2027 parent: -1:0000000000000000000000000000000000000000
2015 2028 user: test
2016 2029 date: [A-Za-z0-9:+ ]+ (re)
2017 2030 files: d1/f1
2018 2031 files+: d1/f2
2019 2032 files-: .d6/f1
2020 2033 extra: branch=default
2021 2034
2022 2035 $ hg log -r 'wdir()' -p --stat --git
2023 2036 changeset: 2147483647:ffffffffffff
2024 2037 parent: 0:65624cd9070a
2025 2038 user: test
2026 2039 date: [A-Za-z0-9:+ ]+ (re)
2027 2040
2028 2041 .d6/f1 | 1 -
2029 2042 d1/f1 | 1 +
2030 2043 d1/f2 | 1 +
2031 2044 3 files changed, 2 insertions(+), 1 deletions(-)
2032 2045
2033 2046 diff --git a/.d6/f1 b/.d6/f1
2034 2047 deleted file mode 100644
2035 2048 --- a/.d6/f1
2036 2049 +++ /dev/null
2037 2050 @@ -1,1 +0,0 @@
2038 2051 -1
2039 2052 diff --git a/d1/f1 b/d1/f1
2040 2053 --- a/d1/f1
2041 2054 +++ b/d1/f1
2042 2055 @@ -1,1 +1,2 @@
2043 2056 1
2044 2057 +2
2045 2058 diff --git a/d1/f2 b/d1/f2
2046 2059 new file mode 100644
2047 2060 --- /dev/null
2048 2061 +++ b/d1/f2
2049 2062 @@ -0,0 +1,1 @@
2050 2063 +2
2051 2064
2052 2065 $ hg log -r 'wdir()' -Tjson
2053 2066 [
2054 2067 {
2055 2068 "rev": null,
2056 2069 "node": null,
2057 2070 "branch": "default",
2058 2071 "phase": "draft",
2059 2072 "user": "test",
2060 2073 "date": [*, 0], (glob)
2061 2074 "desc": "",
2062 2075 "bookmarks": [],
2063 2076 "tags": [],
2064 2077 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"]
2065 2078 }
2066 2079 ]
2067 2080
2068 2081 $ hg log -r 'wdir()' -Tjson -q
2069 2082 [
2070 2083 {
2071 2084 "rev": null,
2072 2085 "node": null
2073 2086 }
2074 2087 ]
2075 2088
2076 2089 $ hg log -r 'wdir()' -Tjson --debug
2077 2090 [
2078 2091 {
2079 2092 "rev": null,
2080 2093 "node": null,
2081 2094 "branch": "default",
2082 2095 "phase": "draft",
2083 2096 "user": "test",
2084 2097 "date": [*, 0], (glob)
2085 2098 "desc": "",
2086 2099 "bookmarks": [],
2087 2100 "tags": [],
2088 2101 "parents": ["65624cd9070a035fa7191a54f2b8af39f16b0c08"],
2089 2102 "manifest": null,
2090 2103 "extra": {"branch": "default"},
2091 2104 "modified": ["d1/f1"],
2092 2105 "added": ["d1/f2"],
2093 2106 "removed": [".d6/f1"]
2094 2107 }
2095 2108 ]
2096 2109
2097 2110 $ hg revert -aqC
2098 2111
2099 2112 Check that adding an arbitrary name shows up in log automatically
2100 2113
2101 2114 $ cat > ../names.py <<EOF
2102 2115 > """A small extension to test adding arbitrary names to a repo"""
2103 2116 > from __future__ import absolute_import
2104 2117 > from mercurial import namespaces
2105 2118 >
2106 2119 > def reposetup(ui, repo):
2107 2120 > foo = {'foo': repo[0].node()}
2108 2121 > names = lambda r: foo.keys()
2109 2122 > namemap = lambda r, name: foo.get(name)
2110 2123 > nodemap = lambda r, node: [name for name, n in foo.iteritems()
2111 2124 > if n == node]
2112 2125 > ns = namespaces.namespace(
2113 2126 > "bars", templatename="bar", logname="barlog",
2114 2127 > colorname="barcolor", listnames=names, namemap=namemap,
2115 2128 > nodemap=nodemap)
2116 2129 >
2117 2130 > repo.names.addnamespace(ns)
2118 2131 > EOF
2119 2132
2120 2133 $ hg --config extensions.names=../names.py log -r 0
2121 2134 changeset: 0:65624cd9070a
2122 2135 tag: tip
2123 2136 barlog: foo
2124 2137 user: test
2125 2138 date: Thu Jan 01 00:00:00 1970 +0000
2126 2139 summary: a bunch of weird directories
2127 2140
2128 2141 $ hg --config extensions.names=../names.py \
2129 2142 > --config extensions.color= --config color.log.barcolor=red \
2130 2143 > --color=always log -r 0
2131 2144 \x1b[0;33mchangeset: 0:65624cd9070a\x1b[0m (esc)
2132 2145 tag: tip
2133 2146 \x1b[0;31mbarlog: foo\x1b[0m (esc)
2134 2147 user: test
2135 2148 date: Thu Jan 01 00:00:00 1970 +0000
2136 2149 summary: a bunch of weird directories
2137 2150
2138 2151 $ hg --config extensions.names=../names.py log -r 0 --template '{bars}\n'
2139 2152 foo
2140 2153
2141 2154 $ cd ..
2142 2155
2143 2156 hg log -f dir across branches
2144 2157
2145 2158 $ hg init acrossbranches
2146 2159 $ cd acrossbranches
2147 2160 $ mkdir d
2148 2161 $ echo a > d/a && hg ci -Aqm a
2149 2162 $ echo b > d/a && hg ci -Aqm b
2150 2163 $ hg up -q 0
2151 2164 $ echo b > d/a && hg ci -Aqm c
2152 2165 $ hg log -f d -T '{desc}' -G
2153 2166 @ c
2154 2167 |
2155 2168 o a
2156 2169
2157 2170 Ensure that largefiles doesn't interfere with following a normal file
2158 2171 $ hg --config extensions.largefiles= log -f d -T '{desc}' -G
2159 2172 The fsmonitor extension is incompatible with the largefiles extension and has been disabled. (fsmonitor !)
2160 2173 @ c
2161 2174 |
2162 2175 o a
2163 2176
2164 2177 $ hg log -f d/a -T '{desc}' -G
2165 2178 @ c
2166 2179 |
2167 2180 o a
2168 2181
2169 2182 $ cd ..
2170 2183
2171 2184 hg log -f with linkrev pointing to another branch
2172 2185 -------------------------------------------------
2173 2186
2174 2187 create history with a filerev whose linkrev points to another branch
2175 2188
2176 2189 $ hg init branchedlinkrev
2177 2190 $ cd branchedlinkrev
2178 2191 $ echo 1 > a
2179 2192 $ hg commit -Am 'content1'
2180 2193 adding a
2181 2194 $ echo 2 > a
2182 2195 $ hg commit -m 'content2'
2183 2196 $ hg up --rev 'desc(content1)'
2184 2197 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2185 2198 $ echo unrelated > unrelated
2186 2199 $ hg commit -Am 'unrelated'
2187 2200 adding unrelated
2188 2201 created new head
2189 2202 $ hg graft -r 'desc(content2)'
2190 2203 grafting 1:2294ae80ad84 "content2"
2191 2204 $ echo 3 > a
2192 2205 $ hg commit -m 'content3'
2193 2206 $ hg log -G
2194 2207 @ changeset: 4:50b9b36e9c5d
2195 2208 | tag: tip
2196 2209 | user: test
2197 2210 | date: Thu Jan 01 00:00:00 1970 +0000
2198 2211 | summary: content3
2199 2212 |
2200 2213 o changeset: 3:15b2327059e5
2201 2214 | user: test
2202 2215 | date: Thu Jan 01 00:00:00 1970 +0000
2203 2216 | summary: content2
2204 2217 |
2205 2218 o changeset: 2:2029acd1168c
2206 2219 | parent: 0:ae0a3c9f9e95
2207 2220 | user: test
2208 2221 | date: Thu Jan 01 00:00:00 1970 +0000
2209 2222 | summary: unrelated
2210 2223 |
2211 2224 | o changeset: 1:2294ae80ad84
2212 2225 |/ user: test
2213 2226 | date: Thu Jan 01 00:00:00 1970 +0000
2214 2227 | summary: content2
2215 2228 |
2216 2229 o changeset: 0:ae0a3c9f9e95
2217 2230 user: test
2218 2231 date: Thu Jan 01 00:00:00 1970 +0000
2219 2232 summary: content1
2220 2233
2221 2234
2222 2235 log -f on the file should list the graft result.
2223 2236
2224 2237 $ hg log -Gf a
2225 2238 @ changeset: 4:50b9b36e9c5d
2226 2239 | tag: tip
2227 2240 | user: test
2228 2241 | date: Thu Jan 01 00:00:00 1970 +0000
2229 2242 | summary: content3
2230 2243 |
2231 2244 o changeset: 3:15b2327059e5
2232 2245 : user: test
2233 2246 : date: Thu Jan 01 00:00:00 1970 +0000
2234 2247 : summary: content2
2235 2248 :
2236 2249 o changeset: 0:ae0a3c9f9e95
2237 2250 user: test
2238 2251 date: Thu Jan 01 00:00:00 1970 +0000
2239 2252 summary: content1
2240 2253
2241 2254
2242 2255 plain log lists the original version
2243 2256 (XXX we should probably list both)
2244 2257
2245 2258 $ hg log -G a
2246 2259 @ changeset: 4:50b9b36e9c5d
2247 2260 : tag: tip
2248 2261 : user: test
2249 2262 : date: Thu Jan 01 00:00:00 1970 +0000
2250 2263 : summary: content3
2251 2264 :
2252 2265 : o changeset: 1:2294ae80ad84
2253 2266 :/ user: test
2254 2267 : date: Thu Jan 01 00:00:00 1970 +0000
2255 2268 : summary: content2
2256 2269 :
2257 2270 o changeset: 0:ae0a3c9f9e95
2258 2271 user: test
2259 2272 date: Thu Jan 01 00:00:00 1970 +0000
2260 2273 summary: content1
2261 2274
2262 2275
2263 2276 hg log -f from the grafted changeset
2264 2277 (The bootstrap should properly take the topology in account)
2265 2278
2266 2279 $ hg up 'desc(content3)^'
2267 2280 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2268 2281 $ hg log -Gf a
2269 2282 @ changeset: 3:15b2327059e5
2270 2283 : user: test
2271 2284 : date: Thu Jan 01 00:00:00 1970 +0000
2272 2285 : summary: content2
2273 2286 :
2274 2287 o changeset: 0:ae0a3c9f9e95
2275 2288 user: test
2276 2289 date: Thu Jan 01 00:00:00 1970 +0000
2277 2290 summary: content1
2278 2291
2279 2292
2280 2293 Test that we use the first non-hidden changeset in that case.
2281 2294
2282 2295 (hide the changeset)
2283 2296
2284 2297 $ hg log -T '{node}\n' -r 1
2285 2298 2294ae80ad8447bc78383182eeac50cb049df623
2286 2299 $ hg debugobsolete 2294ae80ad8447bc78383182eeac50cb049df623
2287 2300 obsoleted 1 changesets
2288 2301 $ hg log -G
2289 2302 o changeset: 4:50b9b36e9c5d
2290 2303 | tag: tip
2291 2304 | user: test
2292 2305 | date: Thu Jan 01 00:00:00 1970 +0000
2293 2306 | summary: content3
2294 2307 |
2295 2308 @ changeset: 3:15b2327059e5
2296 2309 | user: test
2297 2310 | date: Thu Jan 01 00:00:00 1970 +0000
2298 2311 | summary: content2
2299 2312 |
2300 2313 o changeset: 2:2029acd1168c
2301 2314 | parent: 0:ae0a3c9f9e95
2302 2315 | user: test
2303 2316 | date: Thu Jan 01 00:00:00 1970 +0000
2304 2317 | summary: unrelated
2305 2318 |
2306 2319 o changeset: 0:ae0a3c9f9e95
2307 2320 user: test
2308 2321 date: Thu Jan 01 00:00:00 1970 +0000
2309 2322 summary: content1
2310 2323
2311 2324
2312 2325 Check that log on the file does not drop the file revision.
2313 2326
2314 2327 $ hg log -G a
2315 2328 o changeset: 4:50b9b36e9c5d
2316 2329 | tag: tip
2317 2330 | user: test
2318 2331 | date: Thu Jan 01 00:00:00 1970 +0000
2319 2332 | summary: content3
2320 2333 |
2321 2334 @ changeset: 3:15b2327059e5
2322 2335 : user: test
2323 2336 : date: Thu Jan 01 00:00:00 1970 +0000
2324 2337 : summary: content2
2325 2338 :
2326 2339 o changeset: 0:ae0a3c9f9e95
2327 2340 user: test
2328 2341 date: Thu Jan 01 00:00:00 1970 +0000
2329 2342 summary: content1
2330 2343
2331 2344
2332 2345 Even when a head revision is linkrev-shadowed.
2333 2346
2334 2347 $ hg log -T '{node}\n' -r 4
2335 2348 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2336 2349 $ hg debugobsolete 50b9b36e9c5df2c6fc6dcefa8ad0da929e84aed2
2337 2350 obsoleted 1 changesets
2338 2351 $ hg log -G a
2339 2352 @ changeset: 3:15b2327059e5
2340 2353 : tag: tip
2341 2354 : user: test
2342 2355 : date: Thu Jan 01 00:00:00 1970 +0000
2343 2356 : summary: content2
2344 2357 :
2345 2358 o changeset: 0:ae0a3c9f9e95
2346 2359 user: test
2347 2360 date: Thu Jan 01 00:00:00 1970 +0000
2348 2361 summary: content1
2349 2362
2350 2363
2351 2364 $ cd ..
2352 2365
2353 2366 Even when the file revision is missing from some head:
2354 2367
2355 2368 $ hg init issue4490
2356 2369 $ cd issue4490
2357 2370 $ echo '[experimental]' >> .hg/hgrc
2358 2371 $ echo 'evolution.createmarkers=True' >> .hg/hgrc
2359 2372 $ echo a > a
2360 2373 $ hg ci -Am0
2361 2374 adding a
2362 2375 $ echo b > b
2363 2376 $ hg ci -Am1
2364 2377 adding b
2365 2378 $ echo B > b
2366 2379 $ hg ci --amend -m 1
2367 2380 $ hg up 0
2368 2381 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
2369 2382 $ echo c > c
2370 2383 $ hg ci -Am2
2371 2384 adding c
2372 2385 created new head
2373 2386 $ hg up 'head() and not .'
2374 2387 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
2375 2388 $ hg log -G
2376 2389 o changeset: 3:db815d6d32e6
2377 2390 | tag: tip
2378 2391 | parent: 0:f7b1eb17ad24
2379 2392 | user: test
2380 2393 | date: Thu Jan 01 00:00:00 1970 +0000
2381 2394 | summary: 2
2382 2395 |
2383 2396 | @ changeset: 2:9bc8ce7f9356
2384 2397 |/ parent: 0:f7b1eb17ad24
2385 2398 | user: test
2386 2399 | date: Thu Jan 01 00:00:00 1970 +0000
2387 2400 | summary: 1
2388 2401 |
2389 2402 o changeset: 0:f7b1eb17ad24
2390 2403 user: test
2391 2404 date: Thu Jan 01 00:00:00 1970 +0000
2392 2405 summary: 0
2393 2406
2394 2407 $ hg log -f -G b
2395 2408 @ changeset: 2:9bc8ce7f9356
2396 2409 | parent: 0:f7b1eb17ad24
2397 2410 ~ user: test
2398 2411 date: Thu Jan 01 00:00:00 1970 +0000
2399 2412 summary: 1
2400 2413
2401 2414 $ hg log -G b
2402 2415 @ changeset: 2:9bc8ce7f9356
2403 2416 | parent: 0:f7b1eb17ad24
2404 2417 ~ user: test
2405 2418 date: Thu Jan 01 00:00:00 1970 +0000
2406 2419 summary: 1
2407 2420
2408 2421 $ cd ..
2409 2422
2410 2423 Check proper report when the manifest changes but not the file issue4499
2411 2424 ------------------------------------------------------------------------
2412 2425
2413 2426 $ hg init issue4499
2414 2427 $ cd issue4499
2415 2428 $ for f in A B C D F E G H I J K L M N O P Q R S T U; do
2416 2429 > echo 1 > $f;
2417 2430 > hg add $f;
2418 2431 > done
2419 2432 $ hg commit -m 'A1B1C1'
2420 2433 $ echo 2 > A
2421 2434 $ echo 2 > B
2422 2435 $ echo 2 > C
2423 2436 $ hg commit -m 'A2B2C2'
2424 2437 $ hg up 0
2425 2438 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
2426 2439 $ echo 3 > A
2427 2440 $ echo 2 > B
2428 2441 $ echo 2 > C
2429 2442 $ hg commit -m 'A3B2C2'
2430 2443 created new head
2431 2444
2432 2445 $ hg log -G
2433 2446 @ changeset: 2:fe5fc3d0eb17
2434 2447 | tag: tip
2435 2448 | parent: 0:abf4f0e38563
2436 2449 | user: test
2437 2450 | date: Thu Jan 01 00:00:00 1970 +0000
2438 2451 | summary: A3B2C2
2439 2452 |
2440 2453 | o changeset: 1:07dcc6b312c0
2441 2454 |/ user: test
2442 2455 | date: Thu Jan 01 00:00:00 1970 +0000
2443 2456 | summary: A2B2C2
2444 2457 |
2445 2458 o changeset: 0:abf4f0e38563
2446 2459 user: test
2447 2460 date: Thu Jan 01 00:00:00 1970 +0000
2448 2461 summary: A1B1C1
2449 2462
2450 2463
2451 2464 Log -f on B should reports current changesets
2452 2465
2453 2466 $ hg log -fG B
2454 2467 @ changeset: 2:fe5fc3d0eb17
2455 2468 | tag: tip
2456 2469 | parent: 0:abf4f0e38563
2457 2470 | user: test
2458 2471 | date: Thu Jan 01 00:00:00 1970 +0000
2459 2472 | summary: A3B2C2
2460 2473 |
2461 2474 o changeset: 0:abf4f0e38563
2462 2475 user: test
2463 2476 date: Thu Jan 01 00:00:00 1970 +0000
2464 2477 summary: A1B1C1
2465 2478
2466 2479 $ cd ..
General Comments 0
You need to be logged in to leave comments. Login now