##// END OF EJS Templates
obsfate: makes successorsetverb takes the markers as argument...
Boris Feld -
r35011:b81ad5b7 default
parent child Browse files
Show More
@@ -1,837 +1,838 b''
1 1 # obsutil.py - utility functions for obsolescence
2 2 #
3 3 # Copyright 2017 Boris Feld <boris.feld@octobus.net>
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 . import (
13 13 phases,
14 14 util
15 15 )
16 16
17 17 class marker(object):
18 18 """Wrap obsolete marker raw data"""
19 19
20 20 def __init__(self, repo, data):
21 21 # the repo argument will be used to create changectx in later version
22 22 self._repo = repo
23 23 self._data = data
24 24 self._decodedmeta = None
25 25
26 26 def __hash__(self):
27 27 return hash(self._data)
28 28
29 29 def __eq__(self, other):
30 30 if type(other) != type(self):
31 31 return False
32 32 return self._data == other._data
33 33
34 34 def precnode(self):
35 35 msg = ("'marker.precnode' is deprecated, "
36 36 "use 'marker.prednode'")
37 37 util.nouideprecwarn(msg, '4.4')
38 38 return self.prednode()
39 39
40 40 def prednode(self):
41 41 """Predecessor changeset node identifier"""
42 42 return self._data[0]
43 43
44 44 def succnodes(self):
45 45 """List of successor changesets node identifiers"""
46 46 return self._data[1]
47 47
48 48 def parentnodes(self):
49 49 """Parents of the predecessors (None if not recorded)"""
50 50 return self._data[5]
51 51
52 52 def metadata(self):
53 53 """Decoded metadata dictionary"""
54 54 return dict(self._data[3])
55 55
56 56 def date(self):
57 57 """Creation date as (unixtime, offset)"""
58 58 return self._data[4]
59 59
60 60 def flags(self):
61 61 """The flags field of the marker"""
62 62 return self._data[2]
63 63
64 64 def getmarkers(repo, nodes=None, exclusive=False):
65 65 """returns markers known in a repository
66 66
67 67 If <nodes> is specified, only markers "relevant" to those nodes are are
68 68 returned"""
69 69 if nodes is None:
70 70 rawmarkers = repo.obsstore
71 71 elif exclusive:
72 72 rawmarkers = exclusivemarkers(repo, nodes)
73 73 else:
74 74 rawmarkers = repo.obsstore.relevantmarkers(nodes)
75 75
76 76 for markerdata in rawmarkers:
77 77 yield marker(repo, markerdata)
78 78
79 79 def closestpredecessors(repo, nodeid):
80 80 """yield the list of next predecessors pointing on visible changectx nodes
81 81
82 82 This function respect the repoview filtering, filtered revision will be
83 83 considered missing.
84 84 """
85 85
86 86 precursors = repo.obsstore.predecessors
87 87 stack = [nodeid]
88 88 seen = set(stack)
89 89
90 90 while stack:
91 91 current = stack.pop()
92 92 currentpreccs = precursors.get(current, ())
93 93
94 94 for prec in currentpreccs:
95 95 precnodeid = prec[0]
96 96
97 97 # Basic cycle protection
98 98 if precnodeid in seen:
99 99 continue
100 100 seen.add(precnodeid)
101 101
102 102 if precnodeid in repo:
103 103 yield precnodeid
104 104 else:
105 105 stack.append(precnodeid)
106 106
107 107 def allprecursors(*args, **kwargs):
108 108 """ (DEPRECATED)
109 109 """
110 110 msg = ("'obsutil.allprecursors' is deprecated, "
111 111 "use 'obsutil.allpredecessors'")
112 112 util.nouideprecwarn(msg, '4.4')
113 113
114 114 return allpredecessors(*args, **kwargs)
115 115
116 116 def allpredecessors(obsstore, nodes, ignoreflags=0):
117 117 """Yield node for every precursors of <nodes>.
118 118
119 119 Some precursors may be unknown locally.
120 120
121 121 This is a linear yield unsuited to detecting folded changesets. It includes
122 122 initial nodes too."""
123 123
124 124 remaining = set(nodes)
125 125 seen = set(remaining)
126 126 while remaining:
127 127 current = remaining.pop()
128 128 yield current
129 129 for mark in obsstore.predecessors.get(current, ()):
130 130 # ignore marker flagged with specified flag
131 131 if mark[2] & ignoreflags:
132 132 continue
133 133 suc = mark[0]
134 134 if suc not in seen:
135 135 seen.add(suc)
136 136 remaining.add(suc)
137 137
138 138 def allsuccessors(obsstore, nodes, ignoreflags=0):
139 139 """Yield node for every successor of <nodes>.
140 140
141 141 Some successors may be unknown locally.
142 142
143 143 This is a linear yield unsuited to detecting split changesets. It includes
144 144 initial nodes too."""
145 145 remaining = set(nodes)
146 146 seen = set(remaining)
147 147 while remaining:
148 148 current = remaining.pop()
149 149 yield current
150 150 for mark in obsstore.successors.get(current, ()):
151 151 # ignore marker flagged with specified flag
152 152 if mark[2] & ignoreflags:
153 153 continue
154 154 for suc in mark[1]:
155 155 if suc not in seen:
156 156 seen.add(suc)
157 157 remaining.add(suc)
158 158
159 159 def _filterprunes(markers):
160 160 """return a set with no prune markers"""
161 161 return set(m for m in markers if m[1])
162 162
163 163 def exclusivemarkers(repo, nodes):
164 164 """set of markers relevant to "nodes" but no other locally-known nodes
165 165
166 166 This function compute the set of markers "exclusive" to a locally-known
167 167 node. This means we walk the markers starting from <nodes> until we reach a
168 168 locally-known precursors outside of <nodes>. Element of <nodes> with
169 169 locally-known successors outside of <nodes> are ignored (since their
170 170 precursors markers are also relevant to these successors).
171 171
172 172 For example:
173 173
174 174 # (A0 rewritten as A1)
175 175 #
176 176 # A0 <-1- A1 # Marker "1" is exclusive to A1
177 177
178 178 or
179 179
180 180 # (A0 rewritten as AX; AX rewritten as A1; AX is unkown locally)
181 181 #
182 182 # <-1- A0 <-2- AX <-3- A1 # Marker "2,3" are exclusive to A1
183 183
184 184 or
185 185
186 186 # (A0 has unknown precursors, A0 rewritten as A1 and A2 (divergence))
187 187 #
188 188 # <-2- A1 # Marker "2" is exclusive to A0,A1
189 189 # /
190 190 # <-1- A0
191 191 # \
192 192 # <-3- A2 # Marker "3" is exclusive to A0,A2
193 193 #
194 194 # in addition:
195 195 #
196 196 # Markers "2,3" are exclusive to A1,A2
197 197 # Markers "1,2,3" are exclusive to A0,A1,A2
198 198
199 199 See test/test-obsolete-bundle-strip.t for more examples.
200 200
201 201 An example usage is strip. When stripping a changeset, we also want to
202 202 strip the markers exclusive to this changeset. Otherwise we would have
203 203 "dangling"" obsolescence markers from its precursors: Obsolescence markers
204 204 marking a node as obsolete without any successors available locally.
205 205
206 206 As for relevant markers, the prune markers for children will be followed.
207 207 Of course, they will only be followed if the pruned children is
208 208 locally-known. Since the prune markers are relevant to the pruned node.
209 209 However, while prune markers are considered relevant to the parent of the
210 210 pruned changesets, prune markers for locally-known changeset (with no
211 211 successors) are considered exclusive to the pruned nodes. This allows
212 212 to strip the prune markers (with the rest of the exclusive chain) alongside
213 213 the pruned changesets.
214 214 """
215 215 # running on a filtered repository would be dangerous as markers could be
216 216 # reported as exclusive when they are relevant for other filtered nodes.
217 217 unfi = repo.unfiltered()
218 218
219 219 # shortcut to various useful item
220 220 nm = unfi.changelog.nodemap
221 221 precursorsmarkers = unfi.obsstore.predecessors
222 222 successormarkers = unfi.obsstore.successors
223 223 childrenmarkers = unfi.obsstore.children
224 224
225 225 # exclusive markers (return of the function)
226 226 exclmarkers = set()
227 227 # we need fast membership testing
228 228 nodes = set(nodes)
229 229 # looking for head in the obshistory
230 230 #
231 231 # XXX we are ignoring all issues in regard with cycle for now.
232 232 stack = [n for n in nodes if not _filterprunes(successormarkers.get(n, ()))]
233 233 stack.sort()
234 234 # nodes already stacked
235 235 seennodes = set(stack)
236 236 while stack:
237 237 current = stack.pop()
238 238 # fetch precursors markers
239 239 markers = list(precursorsmarkers.get(current, ()))
240 240 # extend the list with prune markers
241 241 for mark in successormarkers.get(current, ()):
242 242 if not mark[1]:
243 243 markers.append(mark)
244 244 # and markers from children (looking for prune)
245 245 for mark in childrenmarkers.get(current, ()):
246 246 if not mark[1]:
247 247 markers.append(mark)
248 248 # traverse the markers
249 249 for mark in markers:
250 250 if mark in exclmarkers:
251 251 # markers already selected
252 252 continue
253 253
254 254 # If the markers is about the current node, select it
255 255 #
256 256 # (this delay the addition of markers from children)
257 257 if mark[1] or mark[0] == current:
258 258 exclmarkers.add(mark)
259 259
260 260 # should we keep traversing through the precursors?
261 261 prec = mark[0]
262 262
263 263 # nodes in the stack or already processed
264 264 if prec in seennodes:
265 265 continue
266 266
267 267 # is this a locally known node ?
268 268 known = prec in nm
269 269 # if locally-known and not in the <nodes> set the traversal
270 270 # stop here.
271 271 if known and prec not in nodes:
272 272 continue
273 273
274 274 # do not keep going if there are unselected markers pointing to this
275 275 # nodes. If we end up traversing these unselected markers later the
276 276 # node will be taken care of at that point.
277 277 precmarkers = _filterprunes(successormarkers.get(prec))
278 278 if precmarkers.issubset(exclmarkers):
279 279 seennodes.add(prec)
280 280 stack.append(prec)
281 281
282 282 return exclmarkers
283 283
284 284 def foreground(repo, nodes):
285 285 """return all nodes in the "foreground" of other node
286 286
287 287 The foreground of a revision is anything reachable using parent -> children
288 288 or precursor -> successor relation. It is very similar to "descendant" but
289 289 augmented with obsolescence information.
290 290
291 291 Beware that possible obsolescence cycle may result if complex situation.
292 292 """
293 293 repo = repo.unfiltered()
294 294 foreground = set(repo.set('%ln::', nodes))
295 295 if repo.obsstore:
296 296 # We only need this complicated logic if there is obsolescence
297 297 # XXX will probably deserve an optimised revset.
298 298 nm = repo.changelog.nodemap
299 299 plen = -1
300 300 # compute the whole set of successors or descendants
301 301 while len(foreground) != plen:
302 302 plen = len(foreground)
303 303 succs = set(c.node() for c in foreground)
304 304 mutable = [c.node() for c in foreground if c.mutable()]
305 305 succs.update(allsuccessors(repo.obsstore, mutable))
306 306 known = (n for n in succs if n in nm)
307 307 foreground = set(repo.set('%ln::', known))
308 308 return set(c.node() for c in foreground)
309 309
310 310 # effectflag field
311 311 #
312 312 # Effect-flag is a 1-byte bit field used to store what changed between a
313 313 # changeset and its successor(s).
314 314 #
315 315 # The effect flag is stored in obs-markers metadata while we iterate on the
316 316 # information design. That's why we have the EFFECTFLAGFIELD. If we come up
317 317 # with an incompatible design for effect flag, we can store a new design under
318 318 # another field name so we don't break readers. We plan to extend the existing
319 319 # obsmarkers bit-field when the effect flag design will be stabilized.
320 320 #
321 321 # The effect-flag is placed behind an experimental flag
322 322 # `effect-flags` set to off by default.
323 323 #
324 324
325 325 EFFECTFLAGFIELD = "ef1"
326 326
327 327 DESCCHANGED = 1 << 0 # action changed the description
328 328 METACHANGED = 1 << 1 # action change the meta
329 329 DIFFCHANGED = 1 << 3 # action change diff introduced by the changeset
330 330 PARENTCHANGED = 1 << 2 # action change the parent
331 331 USERCHANGED = 1 << 4 # the user changed
332 332 DATECHANGED = 1 << 5 # the date changed
333 333 BRANCHCHANGED = 1 << 6 # the branch changed
334 334
335 335 METABLACKLIST = [
336 336 re.compile('^branch$'),
337 337 re.compile('^.*-source$'),
338 338 re.compile('^.*_source$'),
339 339 re.compile('^source$'),
340 340 ]
341 341
342 342 def metanotblacklisted(metaitem):
343 343 """ Check that the key of a meta item (extrakey, extravalue) does not
344 344 match at least one of the blacklist pattern
345 345 """
346 346 metakey = metaitem[0]
347 347
348 348 return not any(pattern.match(metakey) for pattern in METABLACKLIST)
349 349
350 350 def _prepare_hunk(hunk):
351 351 """Drop all information but the username and patch"""
352 352 cleanhunk = []
353 353 for line in hunk.splitlines():
354 354 if line.startswith(b'# User') or not line.startswith(b'#'):
355 355 if line.startswith(b'@@'):
356 356 line = b'@@\n'
357 357 cleanhunk.append(line)
358 358 return cleanhunk
359 359
360 360 def _getdifflines(iterdiff):
361 361 """return a cleaned up lines"""
362 362 lines = next(iterdiff, None)
363 363
364 364 if lines is None:
365 365 return lines
366 366
367 367 return _prepare_hunk(lines)
368 368
369 369 def _cmpdiff(leftctx, rightctx):
370 370 """return True if both ctx introduce the "same diff"
371 371
372 372 This is a first and basic implementation, with many shortcoming.
373 373 """
374 374
375 375 # Leftctx or right ctx might be filtered, so we need to use the contexts
376 376 # with an unfiltered repository to safely compute the diff
377 377 leftunfi = leftctx._repo.unfiltered()[leftctx.rev()]
378 378 leftdiff = leftunfi.diff(git=1)
379 379 rightunfi = rightctx._repo.unfiltered()[rightctx.rev()]
380 380 rightdiff = rightunfi.diff(git=1)
381 381
382 382 left, right = (0, 0)
383 383 while None not in (left, right):
384 384 left = _getdifflines(leftdiff)
385 385 right = _getdifflines(rightdiff)
386 386
387 387 if left != right:
388 388 return False
389 389 return True
390 390
391 391 def geteffectflag(relation):
392 392 """ From an obs-marker relation, compute what changed between the
393 393 predecessor and the successor.
394 394 """
395 395 effects = 0
396 396
397 397 source = relation[0]
398 398
399 399 for changectx in relation[1]:
400 400 # Check if description has changed
401 401 if changectx.description() != source.description():
402 402 effects |= DESCCHANGED
403 403
404 404 # Check if user has changed
405 405 if changectx.user() != source.user():
406 406 effects |= USERCHANGED
407 407
408 408 # Check if date has changed
409 409 if changectx.date() != source.date():
410 410 effects |= DATECHANGED
411 411
412 412 # Check if branch has changed
413 413 if changectx.branch() != source.branch():
414 414 effects |= BRANCHCHANGED
415 415
416 416 # Check if at least one of the parent has changed
417 417 if changectx.parents() != source.parents():
418 418 effects |= PARENTCHANGED
419 419
420 420 # Check if other meta has changed
421 421 changeextra = changectx.extra().items()
422 422 ctxmeta = filter(metanotblacklisted, changeextra)
423 423
424 424 sourceextra = source.extra().items()
425 425 srcmeta = filter(metanotblacklisted, sourceextra)
426 426
427 427 if ctxmeta != srcmeta:
428 428 effects |= METACHANGED
429 429
430 430 # Check if the diff has changed
431 431 if not _cmpdiff(source, changectx):
432 432 effects |= DIFFCHANGED
433 433
434 434 return effects
435 435
436 436 def getobsoleted(repo, tr):
437 437 """return the set of pre-existing revisions obsoleted by a transaction"""
438 438 torev = repo.unfiltered().changelog.nodemap.get
439 439 phase = repo._phasecache.phase
440 440 succsmarkers = repo.obsstore.successors.get
441 441 public = phases.public
442 442 addedmarkers = tr.changes.get('obsmarkers')
443 443 addedrevs = tr.changes.get('revs')
444 444 seenrevs = set(addedrevs)
445 445 obsoleted = set()
446 446 for mark in addedmarkers:
447 447 node = mark[0]
448 448 rev = torev(node)
449 449 if rev is None or rev in seenrevs:
450 450 continue
451 451 seenrevs.add(rev)
452 452 if phase(repo, rev) == public:
453 453 continue
454 454 if set(succsmarkers(node) or []).issubset(addedmarkers):
455 455 obsoleted.add(rev)
456 456 return obsoleted
457 457
458 458 class _succs(list):
459 459 """small class to represent a successors with some metadata about it"""
460 460
461 461 def __init__(self, *args, **kwargs):
462 462 super(_succs, self).__init__(*args, **kwargs)
463 463 self.markers = set()
464 464
465 465 def copy(self):
466 466 new = _succs(self)
467 467 new.markers = self.markers.copy()
468 468 return new
469 469
470 470 @util.propertycache
471 471 def _set(self):
472 472 # immutable
473 473 return set(self)
474 474
475 475 def canmerge(self, other):
476 476 return self._set.issubset(other._set)
477 477
478 478 def successorssets(repo, initialnode, closest=False, cache=None):
479 479 """Return set of all latest successors of initial nodes
480 480
481 481 The successors set of a changeset A are the group of revisions that succeed
482 482 A. It succeeds A as a consistent whole, each revision being only a partial
483 483 replacement. By default, the successors set contains non-obsolete
484 484 changesets only, walking the obsolescence graph until reaching a leaf. If
485 485 'closest' is set to True, closest successors-sets are return (the
486 486 obsolescence walk stops on known changesets).
487 487
488 488 This function returns the full list of successor sets which is why it
489 489 returns a list of tuples and not just a single tuple. Each tuple is a valid
490 490 successors set. Note that (A,) may be a valid successors set for changeset A
491 491 (see below).
492 492
493 493 In most cases, a changeset A will have a single element (e.g. the changeset
494 494 A is replaced by A') in its successors set. Though, it is also common for a
495 495 changeset A to have no elements in its successor set (e.g. the changeset
496 496 has been pruned). Therefore, the returned list of successors sets will be
497 497 [(A',)] or [], respectively.
498 498
499 499 When a changeset A is split into A' and B', however, it will result in a
500 500 successors set containing more than a single element, i.e. [(A',B')].
501 501 Divergent changesets will result in multiple successors sets, i.e. [(A',),
502 502 (A'')].
503 503
504 504 If a changeset A is not obsolete, then it will conceptually have no
505 505 successors set. To distinguish this from a pruned changeset, the successor
506 506 set will contain itself only, i.e. [(A,)].
507 507
508 508 Finally, final successors unknown locally are considered to be pruned
509 509 (pruned: obsoleted without any successors). (Final: successors not affected
510 510 by markers).
511 511
512 512 The 'closest' mode respect the repoview filtering. For example, without
513 513 filter it will stop at the first locally known changeset, with 'visible'
514 514 filter it will stop on visible changesets).
515 515
516 516 The optional `cache` parameter is a dictionary that may contains
517 517 precomputed successors sets. It is meant to reuse the computation of a
518 518 previous call to `successorssets` when multiple calls are made at the same
519 519 time. The cache dictionary is updated in place. The caller is responsible
520 520 for its life span. Code that makes multiple calls to `successorssets`
521 521 *should* use this cache mechanism or risk a performance hit.
522 522
523 523 Since results are different depending of the 'closest' most, the same cache
524 524 cannot be reused for both mode.
525 525 """
526 526
527 527 succmarkers = repo.obsstore.successors
528 528
529 529 # Stack of nodes we search successors sets for
530 530 toproceed = [initialnode]
531 531 # set version of above list for fast loop detection
532 532 # element added to "toproceed" must be added here
533 533 stackedset = set(toproceed)
534 534 if cache is None:
535 535 cache = {}
536 536
537 537 # This while loop is the flattened version of a recursive search for
538 538 # successors sets
539 539 #
540 540 # def successorssets(x):
541 541 # successors = directsuccessors(x)
542 542 # ss = [[]]
543 543 # for succ in directsuccessors(x):
544 544 # # product as in itertools cartesian product
545 545 # ss = product(ss, successorssets(succ))
546 546 # return ss
547 547 #
548 548 # But we can not use plain recursive calls here:
549 549 # - that would blow the python call stack
550 550 # - obsolescence markers may have cycles, we need to handle them.
551 551 #
552 552 # The `toproceed` list act as our call stack. Every node we search
553 553 # successors set for are stacked there.
554 554 #
555 555 # The `stackedset` is set version of this stack used to check if a node is
556 556 # already stacked. This check is used to detect cycles and prevent infinite
557 557 # loop.
558 558 #
559 559 # successors set of all nodes are stored in the `cache` dictionary.
560 560 #
561 561 # After this while loop ends we use the cache to return the successors sets
562 562 # for the node requested by the caller.
563 563 while toproceed:
564 564 # Every iteration tries to compute the successors sets of the topmost
565 565 # node of the stack: CURRENT.
566 566 #
567 567 # There are four possible outcomes:
568 568 #
569 569 # 1) We already know the successors sets of CURRENT:
570 570 # -> mission accomplished, pop it from the stack.
571 571 # 2) Stop the walk:
572 572 # default case: Node is not obsolete
573 573 # closest case: Node is known at this repo filter level
574 574 # -> the node is its own successors sets. Add it to the cache.
575 575 # 3) We do not know successors set of direct successors of CURRENT:
576 576 # -> We add those successors to the stack.
577 577 # 4) We know successors sets of all direct successors of CURRENT:
578 578 # -> We can compute CURRENT successors set and add it to the
579 579 # cache.
580 580 #
581 581 current = toproceed[-1]
582 582
583 583 # case 2 condition is a bit hairy because of closest,
584 584 # we compute it on its own
585 585 case2condition = ((current not in succmarkers)
586 586 or (closest and current != initialnode
587 587 and current in repo))
588 588
589 589 if current in cache:
590 590 # case (1): We already know the successors sets
591 591 stackedset.remove(toproceed.pop())
592 592 elif case2condition:
593 593 # case (2): end of walk.
594 594 if current in repo:
595 595 # We have a valid successors.
596 596 cache[current] = [_succs((current,))]
597 597 else:
598 598 # Final obsolete version is unknown locally.
599 599 # Do not count that as a valid successors
600 600 cache[current] = []
601 601 else:
602 602 # cases (3) and (4)
603 603 #
604 604 # We proceed in two phases. Phase 1 aims to distinguish case (3)
605 605 # from case (4):
606 606 #
607 607 # For each direct successors of CURRENT, we check whether its
608 608 # successors sets are known. If they are not, we stack the
609 609 # unknown node and proceed to the next iteration of the while
610 610 # loop. (case 3)
611 611 #
612 612 # During this step, we may detect obsolescence cycles: a node
613 613 # with unknown successors sets but already in the call stack.
614 614 # In such a situation, we arbitrary set the successors sets of
615 615 # the node to nothing (node pruned) to break the cycle.
616 616 #
617 617 # If no break was encountered we proceed to phase 2.
618 618 #
619 619 # Phase 2 computes successors sets of CURRENT (case 4); see details
620 620 # in phase 2 itself.
621 621 #
622 622 # Note the two levels of iteration in each phase.
623 623 # - The first one handles obsolescence markers using CURRENT as
624 624 # precursor (successors markers of CURRENT).
625 625 #
626 626 # Having multiple entry here means divergence.
627 627 #
628 628 # - The second one handles successors defined in each marker.
629 629 #
630 630 # Having none means pruned node, multiple successors means split,
631 631 # single successors are standard replacement.
632 632 #
633 633 for mark in sorted(succmarkers[current]):
634 634 for suc in mark[1]:
635 635 if suc not in cache:
636 636 if suc in stackedset:
637 637 # cycle breaking
638 638 cache[suc] = []
639 639 else:
640 640 # case (3) If we have not computed successors sets
641 641 # of one of those successors we add it to the
642 642 # `toproceed` stack and stop all work for this
643 643 # iteration.
644 644 toproceed.append(suc)
645 645 stackedset.add(suc)
646 646 break
647 647 else:
648 648 continue
649 649 break
650 650 else:
651 651 # case (4): we know all successors sets of all direct
652 652 # successors
653 653 #
654 654 # Successors set contributed by each marker depends on the
655 655 # successors sets of all its "successors" node.
656 656 #
657 657 # Each different marker is a divergence in the obsolescence
658 658 # history. It contributes successors sets distinct from other
659 659 # markers.
660 660 #
661 661 # Within a marker, a successor may have divergent successors
662 662 # sets. In such a case, the marker will contribute multiple
663 663 # divergent successors sets. If multiple successors have
664 664 # divergent successors sets, a Cartesian product is used.
665 665 #
666 666 # At the end we post-process successors sets to remove
667 667 # duplicated entry and successors set that are strict subset of
668 668 # another one.
669 669 succssets = []
670 670 for mark in sorted(succmarkers[current]):
671 671 # successors sets contributed by this marker
672 672 base = _succs()
673 673 base.markers.add(mark)
674 674 markss = [base]
675 675 for suc in mark[1]:
676 676 # cardinal product with previous successors
677 677 productresult = []
678 678 for prefix in markss:
679 679 for suffix in cache[suc]:
680 680 newss = prefix.copy()
681 681 newss.markers.update(suffix.markers)
682 682 for part in suffix:
683 683 # do not duplicated entry in successors set
684 684 # first entry wins.
685 685 if part not in newss:
686 686 newss.append(part)
687 687 productresult.append(newss)
688 688 markss = productresult
689 689 succssets.extend(markss)
690 690 # remove duplicated and subset
691 691 seen = []
692 692 final = []
693 693 candidates = sorted((s for s in succssets if s),
694 694 key=len, reverse=True)
695 695 for cand in candidates:
696 696 for seensuccs in seen:
697 697 if cand.canmerge(seensuccs):
698 698 seensuccs.markers.update(cand.markers)
699 699 break
700 700 else:
701 701 final.append(cand)
702 702 seen.append(cand)
703 703 final.reverse() # put small successors set first
704 704 cache[current] = final
705 705 return cache[initialnode]
706 706
707 707 def successorsandmarkers(repo, ctx):
708 708 """compute the raw data needed for computing obsfate
709 709 Returns a list of dict, one dict per successors set
710 710 """
711 711 if not ctx.obsolete():
712 712 return None
713 713
714 714 ssets = successorssets(repo, ctx.node(), closest=True)
715 715
716 716 # closestsuccessors returns an empty list for pruned revisions, remap it
717 717 # into a list containing an empty list for future processing
718 718 if ssets == []:
719 719 ssets = [[]]
720 720
721 721 # Try to recover pruned markers
722 722 succsmap = repo.obsstore.successors
723 723 fullsuccessorsets = [] # successor set + markers
724 724 for sset in ssets:
725 725 if sset:
726 726 fullsuccessorsets.append(sset)
727 727 else:
728 728 # successorsset return an empty set() when ctx or one of its
729 729 # successors is pruned.
730 730 # In this case, walk the obs-markers tree again starting with ctx
731 731 # and find the relevant pruning obs-makers, the ones without
732 732 # successors.
733 733 # Having these markers allow us to compute some information about
734 734 # its fate, like who pruned this changeset and when.
735 735
736 736 # XXX we do not catch all prune markers (eg rewritten then pruned)
737 737 # (fix me later)
738 738 foundany = False
739 739 for mark in succsmap.get(ctx.node(), ()):
740 740 if not mark[1]:
741 741 foundany = True
742 742 sset = _succs()
743 743 sset.markers.add(mark)
744 744 fullsuccessorsets.append(sset)
745 745 if not foundany:
746 746 fullsuccessorsets.append(_succs())
747 747
748 748 values = []
749 749 for sset in fullsuccessorsets:
750 750 values.append({'successors': sset, 'markers': sset.markers})
751 751
752 752 return values
753 753
754 def successorsetverb(successorset):
755 """ Return the verb summarizing the successorset
754 def obsfateverb(successorset, markers):
755 """ Return the verb summarizing the successorset and potentially using
756 information from the markers
756 757 """
757 758 if not successorset:
758 759 verb = 'pruned'
759 760 elif len(successorset) == 1:
760 761 verb = 'rewritten'
761 762 else:
762 763 verb = 'split'
763 764 return verb
764 765
765 766 def markersdates(markers):
766 767 """returns the list of dates for a list of markers
767 768 """
768 769 return [m[4] for m in markers]
769 770
770 771 def markersusers(markers):
771 772 """ Returns a sorted list of markers users without duplicates
772 773 """
773 774 markersmeta = [dict(m[3]) for m in markers]
774 775 users = set(meta.get('user') for meta in markersmeta if meta.get('user'))
775 776
776 777 return sorted(users)
777 778
778 779 def markersoperations(markers):
779 780 """ Returns a sorted list of markers operations without duplicates
780 781 """
781 782 markersmeta = [dict(m[3]) for m in markers]
782 783 operations = set(meta.get('operation') for meta in markersmeta
783 784 if meta.get('operation'))
784 785
785 786 return sorted(operations)
786 787
787 788 def obsfateprinter(successors, markers, ui):
788 789 """ Build a obsfate string for a single successorset using all obsfate
789 790 related function defined in obsutil
790 791 """
791 792 quiet = ui.quiet
792 793 verbose = ui.verbose
793 794 normal = not verbose and not quiet
794 795
795 796 line = []
796 797
797 798 # Verb
798 line.append(successorsetverb(successors))
799 line.append(obsfateverb(successors, markers))
799 800
800 801 # Operations
801 802 operations = markersoperations(markers)
802 803 if operations:
803 804 line.append(" using %s" % ", ".join(operations))
804 805
805 806 # Successors
806 807 if successors:
807 808 fmtsuccessors = [successors.joinfmt(succ) for succ in successors]
808 809 line.append(" as %s" % ", ".join(fmtsuccessors))
809 810
810 811 # Users
811 812 users = markersusers(markers)
812 813 # Filter out current user in not verbose mode to reduce amount of
813 814 # information
814 815 if not verbose:
815 816 currentuser = ui.username(acceptempty=True)
816 817 if len(users) == 1 and currentuser in users:
817 818 users = None
818 819
819 820 if (verbose or normal) and users:
820 821 line.append(" by %s" % ", ".join(users))
821 822
822 823 # Date
823 824 dates = markersdates(markers)
824 825
825 826 if dates and verbose:
826 827 min_date = min(dates)
827 828 max_date = max(dates)
828 829
829 830 if min_date == max_date:
830 831 fmtmin_date = util.datestr(min_date, '%Y-%m-%d %H:%M %1%2')
831 832 line.append(" (at %s)" % fmtmin_date)
832 833 else:
833 834 fmtmin_date = util.datestr(min_date, '%Y-%m-%d %H:%M %1%2')
834 835 fmtmax_date = util.datestr(max_date, '%Y-%m-%d %H:%M %1%2')
835 836 line.append(" (between %s and %s)" % (fmtmin_date, fmtmax_date))
836 837
837 838 return "".join(line)
@@ -1,1525 +1,1526 b''
1 1 # templater.py - template expansion for output
2 2 #
3 3 # Copyright 2005, 2006 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, print_function
9 9
10 10 import os
11 11 import re
12 12 import types
13 13
14 14 from .i18n import _
15 15 from . import (
16 16 color,
17 17 config,
18 18 encoding,
19 19 error,
20 20 minirst,
21 21 obsutil,
22 22 parser,
23 23 pycompat,
24 24 registrar,
25 25 revset as revsetmod,
26 26 revsetlang,
27 27 scmutil,
28 28 templatefilters,
29 29 templatekw,
30 30 util,
31 31 )
32 32
33 33 # template parsing
34 34
35 35 elements = {
36 36 # token-type: binding-strength, primary, prefix, infix, suffix
37 37 "(": (20, None, ("group", 1, ")"), ("func", 1, ")"), None),
38 38 ".": (18, None, None, (".", 18), None),
39 39 "%": (15, None, None, ("%", 15), None),
40 40 "|": (15, None, None, ("|", 15), None),
41 41 "*": (5, None, None, ("*", 5), None),
42 42 "/": (5, None, None, ("/", 5), None),
43 43 "+": (4, None, None, ("+", 4), None),
44 44 "-": (4, None, ("negate", 19), ("-", 4), None),
45 45 "=": (3, None, None, ("keyvalue", 3), None),
46 46 ",": (2, None, None, ("list", 2), None),
47 47 ")": (0, None, None, None, None),
48 48 "integer": (0, "integer", None, None, None),
49 49 "symbol": (0, "symbol", None, None, None),
50 50 "string": (0, "string", None, None, None),
51 51 "template": (0, "template", None, None, None),
52 52 "end": (0, None, None, None, None),
53 53 }
54 54
55 55 def tokenize(program, start, end, term=None):
56 56 """Parse a template expression into a stream of tokens, which must end
57 57 with term if specified"""
58 58 pos = start
59 59 program = pycompat.bytestr(program)
60 60 while pos < end:
61 61 c = program[pos]
62 62 if c.isspace(): # skip inter-token whitespace
63 63 pass
64 64 elif c in "(=,).%|+-*/": # handle simple operators
65 65 yield (c, None, pos)
66 66 elif c in '"\'': # handle quoted templates
67 67 s = pos + 1
68 68 data, pos = _parsetemplate(program, s, end, c)
69 69 yield ('template', data, s)
70 70 pos -= 1
71 71 elif c == 'r' and program[pos:pos + 2] in ("r'", 'r"'):
72 72 # handle quoted strings
73 73 c = program[pos + 1]
74 74 s = pos = pos + 2
75 75 while pos < end: # find closing quote
76 76 d = program[pos]
77 77 if d == '\\': # skip over escaped characters
78 78 pos += 2
79 79 continue
80 80 if d == c:
81 81 yield ('string', program[s:pos], s)
82 82 break
83 83 pos += 1
84 84 else:
85 85 raise error.ParseError(_("unterminated string"), s)
86 86 elif c.isdigit():
87 87 s = pos
88 88 while pos < end:
89 89 d = program[pos]
90 90 if not d.isdigit():
91 91 break
92 92 pos += 1
93 93 yield ('integer', program[s:pos], s)
94 94 pos -= 1
95 95 elif (c == '\\' and program[pos:pos + 2] in (r"\'", r'\"')
96 96 or c == 'r' and program[pos:pos + 3] in (r"r\'", r'r\"')):
97 97 # handle escaped quoted strings for compatibility with 2.9.2-3.4,
98 98 # where some of nested templates were preprocessed as strings and
99 99 # then compiled. therefore, \"...\" was allowed. (issue4733)
100 100 #
101 101 # processing flow of _evalifliteral() at 5ab28a2e9962:
102 102 # outer template string -> stringify() -> compiletemplate()
103 103 # ------------------------ ------------ ------------------
104 104 # {f("\\\\ {g(\"\\\"\")}"} \\ {g("\"")} [r'\\', {g("\"")}]
105 105 # ~~~~~~~~
106 106 # escaped quoted string
107 107 if c == 'r':
108 108 pos += 1
109 109 token = 'string'
110 110 else:
111 111 token = 'template'
112 112 quote = program[pos:pos + 2]
113 113 s = pos = pos + 2
114 114 while pos < end: # find closing escaped quote
115 115 if program.startswith('\\\\\\', pos, end):
116 116 pos += 4 # skip over double escaped characters
117 117 continue
118 118 if program.startswith(quote, pos, end):
119 119 # interpret as if it were a part of an outer string
120 120 data = parser.unescapestr(program[s:pos])
121 121 if token == 'template':
122 122 data = _parsetemplate(data, 0, len(data))[0]
123 123 yield (token, data, s)
124 124 pos += 1
125 125 break
126 126 pos += 1
127 127 else:
128 128 raise error.ParseError(_("unterminated string"), s)
129 129 elif c.isalnum() or c in '_':
130 130 s = pos
131 131 pos += 1
132 132 while pos < end: # find end of symbol
133 133 d = program[pos]
134 134 if not (d.isalnum() or d == "_"):
135 135 break
136 136 pos += 1
137 137 sym = program[s:pos]
138 138 yield ('symbol', sym, s)
139 139 pos -= 1
140 140 elif c == term:
141 141 yield ('end', None, pos + 1)
142 142 return
143 143 else:
144 144 raise error.ParseError(_("syntax error"), pos)
145 145 pos += 1
146 146 if term:
147 147 raise error.ParseError(_("unterminated template expansion"), start)
148 148 yield ('end', None, pos)
149 149
150 150 def _parsetemplate(tmpl, start, stop, quote=''):
151 151 r"""
152 152 >>> _parsetemplate(b'foo{bar}"baz', 0, 12)
153 153 ([('string', 'foo'), ('symbol', 'bar'), ('string', '"baz')], 12)
154 154 >>> _parsetemplate(b'foo{bar}"baz', 0, 12, quote=b'"')
155 155 ([('string', 'foo'), ('symbol', 'bar')], 9)
156 156 >>> _parsetemplate(b'foo"{bar}', 0, 9, quote=b'"')
157 157 ([('string', 'foo')], 4)
158 158 >>> _parsetemplate(br'foo\"bar"baz', 0, 12, quote=b'"')
159 159 ([('string', 'foo"'), ('string', 'bar')], 9)
160 160 >>> _parsetemplate(br'foo\\"bar', 0, 10, quote=b'"')
161 161 ([('string', 'foo\\')], 6)
162 162 """
163 163 parsed = []
164 164 sepchars = '{' + quote
165 165 pos = start
166 166 p = parser.parser(elements)
167 167 while pos < stop:
168 168 n = min((tmpl.find(c, pos, stop) for c in sepchars),
169 169 key=lambda n: (n < 0, n))
170 170 if n < 0:
171 171 parsed.append(('string', parser.unescapestr(tmpl[pos:stop])))
172 172 pos = stop
173 173 break
174 174 c = tmpl[n:n + 1]
175 175 bs = (n - pos) - len(tmpl[pos:n].rstrip('\\'))
176 176 if bs % 2 == 1:
177 177 # escaped (e.g. '\{', '\\\{', but not '\\{')
178 178 parsed.append(('string', parser.unescapestr(tmpl[pos:n - 1]) + c))
179 179 pos = n + 1
180 180 continue
181 181 if n > pos:
182 182 parsed.append(('string', parser.unescapestr(tmpl[pos:n])))
183 183 if c == quote:
184 184 return parsed, n + 1
185 185
186 186 parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
187 187 parsed.append(parseres)
188 188
189 189 if quote:
190 190 raise error.ParseError(_("unterminated string"), start)
191 191 return parsed, pos
192 192
193 193 def _unnesttemplatelist(tree):
194 194 """Expand list of templates to node tuple
195 195
196 196 >>> def f(tree):
197 197 ... print(pycompat.sysstr(prettyformat(_unnesttemplatelist(tree))))
198 198 >>> f((b'template', []))
199 199 (string '')
200 200 >>> f((b'template', [(b'string', b'foo')]))
201 201 (string 'foo')
202 202 >>> f((b'template', [(b'string', b'foo'), (b'symbol', b'rev')]))
203 203 (template
204 204 (string 'foo')
205 205 (symbol 'rev'))
206 206 >>> f((b'template', [(b'symbol', b'rev')])) # template(rev) -> str
207 207 (template
208 208 (symbol 'rev'))
209 209 >>> f((b'template', [(b'template', [(b'string', b'foo')])]))
210 210 (string 'foo')
211 211 """
212 212 if not isinstance(tree, tuple):
213 213 return tree
214 214 op = tree[0]
215 215 if op != 'template':
216 216 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
217 217
218 218 assert len(tree) == 2
219 219 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
220 220 if not xs:
221 221 return ('string', '') # empty template ""
222 222 elif len(xs) == 1 and xs[0][0] == 'string':
223 223 return xs[0] # fast path for string with no template fragment "x"
224 224 else:
225 225 return (op,) + xs
226 226
227 227 def parse(tmpl):
228 228 """Parse template string into tree"""
229 229 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
230 230 assert pos == len(tmpl), 'unquoted template should be consumed'
231 231 return _unnesttemplatelist(('template', parsed))
232 232
233 233 def _parseexpr(expr):
234 234 """Parse a template expression into tree
235 235
236 236 >>> _parseexpr(b'"foo"')
237 237 ('string', 'foo')
238 238 >>> _parseexpr(b'foo(bar)')
239 239 ('func', ('symbol', 'foo'), ('symbol', 'bar'))
240 240 >>> _parseexpr(b'foo(')
241 241 Traceback (most recent call last):
242 242 ...
243 243 ParseError: ('not a prefix: end', 4)
244 244 >>> _parseexpr(b'"foo" "bar"')
245 245 Traceback (most recent call last):
246 246 ...
247 247 ParseError: ('invalid token', 7)
248 248 """
249 249 p = parser.parser(elements)
250 250 tree, pos = p.parse(tokenize(expr, 0, len(expr)))
251 251 if pos != len(expr):
252 252 raise error.ParseError(_('invalid token'), pos)
253 253 return _unnesttemplatelist(tree)
254 254
255 255 def prettyformat(tree):
256 256 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
257 257
258 258 def compileexp(exp, context, curmethods):
259 259 """Compile parsed template tree to (func, data) pair"""
260 260 t = exp[0]
261 261 if t in curmethods:
262 262 return curmethods[t](exp, context)
263 263 raise error.ParseError(_("unknown method '%s'") % t)
264 264
265 265 # template evaluation
266 266
267 267 def getsymbol(exp):
268 268 if exp[0] == 'symbol':
269 269 return exp[1]
270 270 raise error.ParseError(_("expected a symbol, got '%s'") % exp[0])
271 271
272 272 def getlist(x):
273 273 if not x:
274 274 return []
275 275 if x[0] == 'list':
276 276 return getlist(x[1]) + [x[2]]
277 277 return [x]
278 278
279 279 def gettemplate(exp, context):
280 280 """Compile given template tree or load named template from map file;
281 281 returns (func, data) pair"""
282 282 if exp[0] in ('template', 'string'):
283 283 return compileexp(exp, context, methods)
284 284 if exp[0] == 'symbol':
285 285 # unlike runsymbol(), here 'symbol' is always taken as template name
286 286 # even if it exists in mapping. this allows us to override mapping
287 287 # by web templates, e.g. 'changelogtag' is redefined in map file.
288 288 return context._load(exp[1])
289 289 raise error.ParseError(_("expected template specifier"))
290 290
291 291 def findsymbolicname(arg):
292 292 """Find symbolic name for the given compiled expression; returns None
293 293 if nothing found reliably"""
294 294 while True:
295 295 func, data = arg
296 296 if func is runsymbol:
297 297 return data
298 298 elif func is runfilter:
299 299 arg = data[0]
300 300 else:
301 301 return None
302 302
303 303 def evalrawexp(context, mapping, arg):
304 304 """Evaluate given argument as a bare template object which may require
305 305 further processing (such as folding generator of strings)"""
306 306 func, data = arg
307 307 return func(context, mapping, data)
308 308
309 309 def evalfuncarg(context, mapping, arg):
310 310 """Evaluate given argument as value type"""
311 311 thing = evalrawexp(context, mapping, arg)
312 312 thing = templatekw.unwrapvalue(thing)
313 313 # evalrawexp() may return string, generator of strings or arbitrary object
314 314 # such as date tuple, but filter does not want generator.
315 315 if isinstance(thing, types.GeneratorType):
316 316 thing = stringify(thing)
317 317 return thing
318 318
319 319 def evalboolean(context, mapping, arg):
320 320 """Evaluate given argument as boolean, but also takes boolean literals"""
321 321 func, data = arg
322 322 if func is runsymbol:
323 323 thing = func(context, mapping, data, default=None)
324 324 if thing is None:
325 325 # not a template keyword, takes as a boolean literal
326 326 thing = util.parsebool(data)
327 327 else:
328 328 thing = func(context, mapping, data)
329 329 thing = templatekw.unwrapvalue(thing)
330 330 if isinstance(thing, bool):
331 331 return thing
332 332 # other objects are evaluated as strings, which means 0 is True, but
333 333 # empty dict/list should be False as they are expected to be ''
334 334 return bool(stringify(thing))
335 335
336 336 def evalinteger(context, mapping, arg, err=None):
337 337 v = evalfuncarg(context, mapping, arg)
338 338 try:
339 339 return int(v)
340 340 except (TypeError, ValueError):
341 341 raise error.ParseError(err or _('not an integer'))
342 342
343 343 def evalstring(context, mapping, arg):
344 344 return stringify(evalrawexp(context, mapping, arg))
345 345
346 346 def evalstringliteral(context, mapping, arg):
347 347 """Evaluate given argument as string template, but returns symbol name
348 348 if it is unknown"""
349 349 func, data = arg
350 350 if func is runsymbol:
351 351 thing = func(context, mapping, data, default=data)
352 352 else:
353 353 thing = func(context, mapping, data)
354 354 return stringify(thing)
355 355
356 356 _evalfuncbytype = {
357 357 bool: evalboolean,
358 358 bytes: evalstring,
359 359 int: evalinteger,
360 360 }
361 361
362 362 def evalastype(context, mapping, arg, typ):
363 363 """Evaluate given argument and coerce its type"""
364 364 try:
365 365 f = _evalfuncbytype[typ]
366 366 except KeyError:
367 367 raise error.ProgrammingError('invalid type specified: %r' % typ)
368 368 return f(context, mapping, arg)
369 369
370 370 def runinteger(context, mapping, data):
371 371 return int(data)
372 372
373 373 def runstring(context, mapping, data):
374 374 return data
375 375
376 376 def _recursivesymbolblocker(key):
377 377 def showrecursion(**args):
378 378 raise error.Abort(_("recursive reference '%s' in template") % key)
379 379 return showrecursion
380 380
381 381 def _runrecursivesymbol(context, mapping, key):
382 382 raise error.Abort(_("recursive reference '%s' in template") % key)
383 383
384 384 def runsymbol(context, mapping, key, default=''):
385 385 v = mapping.get(key)
386 386 if v is None:
387 387 v = context._defaults.get(key)
388 388 if v is None:
389 389 # put poison to cut recursion. we can't move this to parsing phase
390 390 # because "x = {x}" is allowed if "x" is a keyword. (issue4758)
391 391 safemapping = mapping.copy()
392 392 safemapping[key] = _recursivesymbolblocker(key)
393 393 try:
394 394 v = context.process(key, safemapping)
395 395 except TemplateNotFound:
396 396 v = default
397 397 if callable(v):
398 398 return v(**pycompat.strkwargs(mapping))
399 399 return v
400 400
401 401 def buildtemplate(exp, context):
402 402 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
403 403 return (runtemplate, ctmpl)
404 404
405 405 def runtemplate(context, mapping, template):
406 406 for arg in template:
407 407 yield evalrawexp(context, mapping, arg)
408 408
409 409 def buildfilter(exp, context):
410 410 n = getsymbol(exp[2])
411 411 if n in context._filters:
412 412 filt = context._filters[n]
413 413 arg = compileexp(exp[1], context, methods)
414 414 return (runfilter, (arg, filt))
415 415 if n in funcs:
416 416 f = funcs[n]
417 417 args = _buildfuncargs(exp[1], context, methods, n, f._argspec)
418 418 return (f, args)
419 419 raise error.ParseError(_("unknown function '%s'") % n)
420 420
421 421 def runfilter(context, mapping, data):
422 422 arg, filt = data
423 423 thing = evalfuncarg(context, mapping, arg)
424 424 try:
425 425 return filt(thing)
426 426 except (ValueError, AttributeError, TypeError):
427 427 sym = findsymbolicname(arg)
428 428 if sym:
429 429 msg = (_("template filter '%s' is not compatible with keyword '%s'")
430 430 % (pycompat.sysbytes(filt.__name__), sym))
431 431 else:
432 432 msg = (_("incompatible use of template filter '%s'")
433 433 % pycompat.sysbytes(filt.__name__))
434 434 raise error.Abort(msg)
435 435
436 436 def buildmap(exp, context):
437 437 darg = compileexp(exp[1], context, methods)
438 438 targ = gettemplate(exp[2], context)
439 439 return (runmap, (darg, targ))
440 440
441 441 def runmap(context, mapping, data):
442 442 darg, targ = data
443 443 d = evalrawexp(context, mapping, darg)
444 444 if util.safehasattr(d, 'itermaps'):
445 445 diter = d.itermaps()
446 446 else:
447 447 try:
448 448 diter = iter(d)
449 449 except TypeError:
450 450 sym = findsymbolicname(darg)
451 451 if sym:
452 452 raise error.ParseError(_("keyword '%s' is not iterable") % sym)
453 453 else:
454 454 raise error.ParseError(_("%r is not iterable") % d)
455 455
456 456 for i, v in enumerate(diter):
457 457 lm = mapping.copy()
458 458 lm['index'] = i
459 459 if isinstance(v, dict):
460 460 lm.update(v)
461 461 lm['originalnode'] = mapping.get('node')
462 462 yield evalrawexp(context, lm, targ)
463 463 else:
464 464 # v is not an iterable of dicts, this happen when 'key'
465 465 # has been fully expanded already and format is useless.
466 466 # If so, return the expanded value.
467 467 yield v
468 468
469 469 def buildmember(exp, context):
470 470 darg = compileexp(exp[1], context, methods)
471 471 memb = getsymbol(exp[2])
472 472 return (runmember, (darg, memb))
473 473
474 474 def runmember(context, mapping, data):
475 475 darg, memb = data
476 476 d = evalrawexp(context, mapping, darg)
477 477 if util.safehasattr(d, 'tomap'):
478 478 lm = mapping.copy()
479 479 lm.update(d.tomap())
480 480 return runsymbol(context, lm, memb)
481 481 if util.safehasattr(d, 'get'):
482 482 return _getdictitem(d, memb)
483 483
484 484 sym = findsymbolicname(darg)
485 485 if sym:
486 486 raise error.ParseError(_("keyword '%s' has no member") % sym)
487 487 else:
488 488 raise error.ParseError(_("%r has no member") % d)
489 489
490 490 def buildnegate(exp, context):
491 491 arg = compileexp(exp[1], context, exprmethods)
492 492 return (runnegate, arg)
493 493
494 494 def runnegate(context, mapping, data):
495 495 data = evalinteger(context, mapping, data,
496 496 _('negation needs an integer argument'))
497 497 return -data
498 498
499 499 def buildarithmetic(exp, context, func):
500 500 left = compileexp(exp[1], context, exprmethods)
501 501 right = compileexp(exp[2], context, exprmethods)
502 502 return (runarithmetic, (func, left, right))
503 503
504 504 def runarithmetic(context, mapping, data):
505 505 func, left, right = data
506 506 left = evalinteger(context, mapping, left,
507 507 _('arithmetic only defined on integers'))
508 508 right = evalinteger(context, mapping, right,
509 509 _('arithmetic only defined on integers'))
510 510 try:
511 511 return func(left, right)
512 512 except ZeroDivisionError:
513 513 raise error.Abort(_('division by zero is not defined'))
514 514
515 515 def buildfunc(exp, context):
516 516 n = getsymbol(exp[1])
517 517 if n in funcs:
518 518 f = funcs[n]
519 519 args = _buildfuncargs(exp[2], context, exprmethods, n, f._argspec)
520 520 return (f, args)
521 521 if n in context._filters:
522 522 args = _buildfuncargs(exp[2], context, exprmethods, n, argspec=None)
523 523 if len(args) != 1:
524 524 raise error.ParseError(_("filter %s expects one argument") % n)
525 525 f = context._filters[n]
526 526 return (runfilter, (args[0], f))
527 527 raise error.ParseError(_("unknown function '%s'") % n)
528 528
529 529 def _buildfuncargs(exp, context, curmethods, funcname, argspec):
530 530 """Compile parsed tree of function arguments into list or dict of
531 531 (func, data) pairs
532 532
533 533 >>> context = engine(lambda t: (runsymbol, t))
534 534 >>> def fargs(expr, argspec):
535 535 ... x = _parseexpr(expr)
536 536 ... n = getsymbol(x[1])
537 537 ... return _buildfuncargs(x[2], context, exprmethods, n, argspec)
538 538 >>> list(fargs(b'a(l=1, k=2)', b'k l m').keys())
539 539 ['l', 'k']
540 540 >>> args = fargs(b'a(opts=1, k=2)', b'**opts')
541 541 >>> list(args.keys()), list(args[b'opts'].keys())
542 542 (['opts'], ['opts', 'k'])
543 543 """
544 544 def compiledict(xs):
545 545 return util.sortdict((k, compileexp(x, context, curmethods))
546 546 for k, x in xs.iteritems())
547 547 def compilelist(xs):
548 548 return [compileexp(x, context, curmethods) for x in xs]
549 549
550 550 if not argspec:
551 551 # filter or function with no argspec: return list of positional args
552 552 return compilelist(getlist(exp))
553 553
554 554 # function with argspec: return dict of named args
555 555 _poskeys, varkey, _keys, optkey = argspec = parser.splitargspec(argspec)
556 556 treeargs = parser.buildargsdict(getlist(exp), funcname, argspec,
557 557 keyvaluenode='keyvalue', keynode='symbol')
558 558 compargs = util.sortdict()
559 559 if varkey:
560 560 compargs[varkey] = compilelist(treeargs.pop(varkey))
561 561 if optkey:
562 562 compargs[optkey] = compiledict(treeargs.pop(optkey))
563 563 compargs.update(compiledict(treeargs))
564 564 return compargs
565 565
566 566 def buildkeyvaluepair(exp, content):
567 567 raise error.ParseError(_("can't use a key-value pair in this context"))
568 568
569 569 # dict of template built-in functions
570 570 funcs = {}
571 571
572 572 templatefunc = registrar.templatefunc(funcs)
573 573
574 574 @templatefunc('date(date[, fmt])')
575 575 def date(context, mapping, args):
576 576 """Format a date. See :hg:`help dates` for formatting
577 577 strings. The default is a Unix date format, including the timezone:
578 578 "Mon Sep 04 15:13:13 2006 0700"."""
579 579 if not (1 <= len(args) <= 2):
580 580 # i18n: "date" is a keyword
581 581 raise error.ParseError(_("date expects one or two arguments"))
582 582
583 583 date = evalfuncarg(context, mapping, args[0])
584 584 fmt = None
585 585 if len(args) == 2:
586 586 fmt = evalstring(context, mapping, args[1])
587 587 try:
588 588 if fmt is None:
589 589 return util.datestr(date)
590 590 else:
591 591 return util.datestr(date, fmt)
592 592 except (TypeError, ValueError):
593 593 # i18n: "date" is a keyword
594 594 raise error.ParseError(_("date expects a date information"))
595 595
596 596 @templatefunc('dict([[key=]value...])', argspec='*args **kwargs')
597 597 def dict_(context, mapping, args):
598 598 """Construct a dict from key-value pairs. A key may be omitted if
599 599 a value expression can provide an unambiguous name."""
600 600 data = util.sortdict()
601 601
602 602 for v in args['args']:
603 603 k = findsymbolicname(v)
604 604 if not k:
605 605 raise error.ParseError(_('dict key cannot be inferred'))
606 606 if k in data or k in args['kwargs']:
607 607 raise error.ParseError(_("duplicated dict key '%s' inferred") % k)
608 608 data[k] = evalfuncarg(context, mapping, v)
609 609
610 610 data.update((k, evalfuncarg(context, mapping, v))
611 611 for k, v in args['kwargs'].iteritems())
612 612 return templatekw.hybriddict(data)
613 613
614 614 @templatefunc('diff([includepattern [, excludepattern]])')
615 615 def diff(context, mapping, args):
616 616 """Show a diff, optionally
617 617 specifying files to include or exclude."""
618 618 if len(args) > 2:
619 619 # i18n: "diff" is a keyword
620 620 raise error.ParseError(_("diff expects zero, one, or two arguments"))
621 621
622 622 def getpatterns(i):
623 623 if i < len(args):
624 624 s = evalstring(context, mapping, args[i]).strip()
625 625 if s:
626 626 return [s]
627 627 return []
628 628
629 629 ctx = mapping['ctx']
630 630 chunks = ctx.diff(match=ctx.match([], getpatterns(0), getpatterns(1)))
631 631
632 632 return ''.join(chunks)
633 633
634 634 @templatefunc('extdata(source)', argspec='source')
635 635 def extdata(context, mapping, args):
636 636 """Show a text read from the specified extdata source. (EXPERIMENTAL)"""
637 637 if 'source' not in args:
638 638 # i18n: "extdata" is a keyword
639 639 raise error.ParseError(_('extdata expects one argument'))
640 640
641 641 source = evalstring(context, mapping, args['source'])
642 642 cache = mapping['cache'].setdefault('extdata', {})
643 643 ctx = mapping['ctx']
644 644 if source in cache:
645 645 data = cache[source]
646 646 else:
647 647 data = cache[source] = scmutil.extdatasource(ctx.repo(), source)
648 648 return data.get(ctx.rev(), '')
649 649
650 650 @templatefunc('files(pattern)')
651 651 def files(context, mapping, args):
652 652 """All files of the current changeset matching the pattern. See
653 653 :hg:`help patterns`."""
654 654 if not len(args) == 1:
655 655 # i18n: "files" is a keyword
656 656 raise error.ParseError(_("files expects one argument"))
657 657
658 658 raw = evalstring(context, mapping, args[0])
659 659 ctx = mapping['ctx']
660 660 m = ctx.match([raw])
661 661 files = list(ctx.matches(m))
662 662 return templatekw.showlist("file", files, mapping)
663 663
664 664 @templatefunc('fill(text[, width[, initialident[, hangindent]]])')
665 665 def fill(context, mapping, args):
666 666 """Fill many
667 667 paragraphs with optional indentation. See the "fill" filter."""
668 668 if not (1 <= len(args) <= 4):
669 669 # i18n: "fill" is a keyword
670 670 raise error.ParseError(_("fill expects one to four arguments"))
671 671
672 672 text = evalstring(context, mapping, args[0])
673 673 width = 76
674 674 initindent = ''
675 675 hangindent = ''
676 676 if 2 <= len(args) <= 4:
677 677 width = evalinteger(context, mapping, args[1],
678 678 # i18n: "fill" is a keyword
679 679 _("fill expects an integer width"))
680 680 try:
681 681 initindent = evalstring(context, mapping, args[2])
682 682 hangindent = evalstring(context, mapping, args[3])
683 683 except IndexError:
684 684 pass
685 685
686 686 return templatefilters.fill(text, width, initindent, hangindent)
687 687
688 688 @templatefunc('formatnode(node)')
689 689 def formatnode(context, mapping, args):
690 690 """Obtain the preferred form of a changeset hash. (DEPRECATED)"""
691 691 if len(args) != 1:
692 692 # i18n: "formatnode" is a keyword
693 693 raise error.ParseError(_("formatnode expects one argument"))
694 694
695 695 ui = mapping['ui']
696 696 node = evalstring(context, mapping, args[0])
697 697 if ui.debugflag:
698 698 return node
699 699 return templatefilters.short(node)
700 700
701 701 @templatefunc('pad(text, width[, fillchar=\' \'[, left=False]])',
702 702 argspec='text width fillchar left')
703 703 def pad(context, mapping, args):
704 704 """Pad text with a
705 705 fill character."""
706 706 if 'text' not in args or 'width' not in args:
707 707 # i18n: "pad" is a keyword
708 708 raise error.ParseError(_("pad() expects two to four arguments"))
709 709
710 710 width = evalinteger(context, mapping, args['width'],
711 711 # i18n: "pad" is a keyword
712 712 _("pad() expects an integer width"))
713 713
714 714 text = evalstring(context, mapping, args['text'])
715 715
716 716 left = False
717 717 fillchar = ' '
718 718 if 'fillchar' in args:
719 719 fillchar = evalstring(context, mapping, args['fillchar'])
720 720 if len(color.stripeffects(fillchar)) != 1:
721 721 # i18n: "pad" is a keyword
722 722 raise error.ParseError(_("pad() expects a single fill character"))
723 723 if 'left' in args:
724 724 left = evalboolean(context, mapping, args['left'])
725 725
726 726 fillwidth = width - encoding.colwidth(color.stripeffects(text))
727 727 if fillwidth <= 0:
728 728 return text
729 729 if left:
730 730 return fillchar * fillwidth + text
731 731 else:
732 732 return text + fillchar * fillwidth
733 733
734 734 @templatefunc('indent(text, indentchars[, firstline])')
735 735 def indent(context, mapping, args):
736 736 """Indents all non-empty lines
737 737 with the characters given in the indentchars string. An optional
738 738 third parameter will override the indent for the first line only
739 739 if present."""
740 740 if not (2 <= len(args) <= 3):
741 741 # i18n: "indent" is a keyword
742 742 raise error.ParseError(_("indent() expects two or three arguments"))
743 743
744 744 text = evalstring(context, mapping, args[0])
745 745 indent = evalstring(context, mapping, args[1])
746 746
747 747 if len(args) == 3:
748 748 firstline = evalstring(context, mapping, args[2])
749 749 else:
750 750 firstline = indent
751 751
752 752 # the indent function doesn't indent the first line, so we do it here
753 753 return templatefilters.indent(firstline + text, indent)
754 754
755 755 @templatefunc('get(dict, key)')
756 756 def get(context, mapping, args):
757 757 """Get an attribute/key from an object. Some keywords
758 758 are complex types. This function allows you to obtain the value of an
759 759 attribute on these types."""
760 760 if len(args) != 2:
761 761 # i18n: "get" is a keyword
762 762 raise error.ParseError(_("get() expects two arguments"))
763 763
764 764 dictarg = evalfuncarg(context, mapping, args[0])
765 765 if not util.safehasattr(dictarg, 'get'):
766 766 # i18n: "get" is a keyword
767 767 raise error.ParseError(_("get() expects a dict as first argument"))
768 768
769 769 key = evalfuncarg(context, mapping, args[1])
770 770 return _getdictitem(dictarg, key)
771 771
772 772 def _getdictitem(dictarg, key):
773 773 val = dictarg.get(key)
774 774 if val is None:
775 775 return
776 776 return templatekw.wraphybridvalue(dictarg, key, val)
777 777
778 778 @templatefunc('if(expr, then[, else])')
779 779 def if_(context, mapping, args):
780 780 """Conditionally execute based on the result of
781 781 an expression."""
782 782 if not (2 <= len(args) <= 3):
783 783 # i18n: "if" is a keyword
784 784 raise error.ParseError(_("if expects two or three arguments"))
785 785
786 786 test = evalboolean(context, mapping, args[0])
787 787 if test:
788 788 yield evalrawexp(context, mapping, args[1])
789 789 elif len(args) == 3:
790 790 yield evalrawexp(context, mapping, args[2])
791 791
792 792 @templatefunc('ifcontains(needle, haystack, then[, else])')
793 793 def ifcontains(context, mapping, args):
794 794 """Conditionally execute based
795 795 on whether the item "needle" is in "haystack"."""
796 796 if not (3 <= len(args) <= 4):
797 797 # i18n: "ifcontains" is a keyword
798 798 raise error.ParseError(_("ifcontains expects three or four arguments"))
799 799
800 800 haystack = evalfuncarg(context, mapping, args[1])
801 801 try:
802 802 needle = evalastype(context, mapping, args[0],
803 803 getattr(haystack, 'keytype', None) or bytes)
804 804 found = (needle in haystack)
805 805 except error.ParseError:
806 806 found = False
807 807
808 808 if found:
809 809 yield evalrawexp(context, mapping, args[2])
810 810 elif len(args) == 4:
811 811 yield evalrawexp(context, mapping, args[3])
812 812
813 813 @templatefunc('ifeq(expr1, expr2, then[, else])')
814 814 def ifeq(context, mapping, args):
815 815 """Conditionally execute based on
816 816 whether 2 items are equivalent."""
817 817 if not (3 <= len(args) <= 4):
818 818 # i18n: "ifeq" is a keyword
819 819 raise error.ParseError(_("ifeq expects three or four arguments"))
820 820
821 821 test = evalstring(context, mapping, args[0])
822 822 match = evalstring(context, mapping, args[1])
823 823 if test == match:
824 824 yield evalrawexp(context, mapping, args[2])
825 825 elif len(args) == 4:
826 826 yield evalrawexp(context, mapping, args[3])
827 827
828 828 @templatefunc('join(list, sep)')
829 829 def join(context, mapping, args):
830 830 """Join items in a list with a delimiter."""
831 831 if not (1 <= len(args) <= 2):
832 832 # i18n: "join" is a keyword
833 833 raise error.ParseError(_("join expects one or two arguments"))
834 834
835 835 # TODO: perhaps this should be evalfuncarg(), but it can't because hgweb
836 836 # abuses generator as a keyword that returns a list of dicts.
837 837 joinset = evalrawexp(context, mapping, args[0])
838 838 joinset = templatekw.unwrapvalue(joinset)
839 839 joinfmt = getattr(joinset, 'joinfmt', pycompat.identity)
840 840 joiner = " "
841 841 if len(args) > 1:
842 842 joiner = evalstring(context, mapping, args[1])
843 843
844 844 first = True
845 845 for x in joinset:
846 846 if first:
847 847 first = False
848 848 else:
849 849 yield joiner
850 850 yield joinfmt(x)
851 851
852 852 @templatefunc('label(label, expr)')
853 853 def label(context, mapping, args):
854 854 """Apply a label to generated content. Content with
855 855 a label applied can result in additional post-processing, such as
856 856 automatic colorization."""
857 857 if len(args) != 2:
858 858 # i18n: "label" is a keyword
859 859 raise error.ParseError(_("label expects two arguments"))
860 860
861 861 ui = mapping['ui']
862 862 thing = evalstring(context, mapping, args[1])
863 863 # preserve unknown symbol as literal so effects like 'red', 'bold',
864 864 # etc. don't need to be quoted
865 865 label = evalstringliteral(context, mapping, args[0])
866 866
867 867 return ui.label(thing, label)
868 868
869 869 @templatefunc('latesttag([pattern])')
870 870 def latesttag(context, mapping, args):
871 871 """The global tags matching the given pattern on the
872 872 most recent globally tagged ancestor of this changeset.
873 873 If no such tags exist, the "{tag}" template resolves to
874 874 the string "null"."""
875 875 if len(args) > 1:
876 876 # i18n: "latesttag" is a keyword
877 877 raise error.ParseError(_("latesttag expects at most one argument"))
878 878
879 879 pattern = None
880 880 if len(args) == 1:
881 881 pattern = evalstring(context, mapping, args[0])
882 882
883 883 return templatekw.showlatesttags(pattern, **mapping)
884 884
885 885 @templatefunc('localdate(date[, tz])')
886 886 def localdate(context, mapping, args):
887 887 """Converts a date to the specified timezone.
888 888 The default is local date."""
889 889 if not (1 <= len(args) <= 2):
890 890 # i18n: "localdate" is a keyword
891 891 raise error.ParseError(_("localdate expects one or two arguments"))
892 892
893 893 date = evalfuncarg(context, mapping, args[0])
894 894 try:
895 895 date = util.parsedate(date)
896 896 except AttributeError: # not str nor date tuple
897 897 # i18n: "localdate" is a keyword
898 898 raise error.ParseError(_("localdate expects a date information"))
899 899 if len(args) >= 2:
900 900 tzoffset = None
901 901 tz = evalfuncarg(context, mapping, args[1])
902 902 if isinstance(tz, str):
903 903 tzoffset, remainder = util.parsetimezone(tz)
904 904 if remainder:
905 905 tzoffset = None
906 906 if tzoffset is None:
907 907 try:
908 908 tzoffset = int(tz)
909 909 except (TypeError, ValueError):
910 910 # i18n: "localdate" is a keyword
911 911 raise error.ParseError(_("localdate expects a timezone"))
912 912 else:
913 913 tzoffset = util.makedate()[1]
914 914 return (date[0], tzoffset)
915 915
916 916 @templatefunc('max(iterable)')
917 917 def max_(context, mapping, args, **kwargs):
918 918 """Return the max of an iterable"""
919 919 if len(args) != 1:
920 920 # i18n: "max" is a keyword
921 921 raise error.ParseError(_("max expects one arguments"))
922 922
923 923 iterable = evalfuncarg(context, mapping, args[0])
924 924 try:
925 925 x = max(iterable)
926 926 except (TypeError, ValueError):
927 927 # i18n: "max" is a keyword
928 928 raise error.ParseError(_("max first argument should be an iterable"))
929 929 return templatekw.wraphybridvalue(iterable, x, x)
930 930
931 931 @templatefunc('min(iterable)')
932 932 def min_(context, mapping, args, **kwargs):
933 933 """Return the min of an iterable"""
934 934 if len(args) != 1:
935 935 # i18n: "min" is a keyword
936 936 raise error.ParseError(_("min expects one arguments"))
937 937
938 938 iterable = evalfuncarg(context, mapping, args[0])
939 939 try:
940 940 x = min(iterable)
941 941 except (TypeError, ValueError):
942 942 # i18n: "min" is a keyword
943 943 raise error.ParseError(_("min first argument should be an iterable"))
944 944 return templatekw.wraphybridvalue(iterable, x, x)
945 945
946 946 @templatefunc('mod(a, b)')
947 947 def mod(context, mapping, args):
948 948 """Calculate a mod b such that a / b + a mod b == a"""
949 949 if not len(args) == 2:
950 950 # i18n: "mod" is a keyword
951 951 raise error.ParseError(_("mod expects two arguments"))
952 952
953 953 func = lambda a, b: a % b
954 954 return runarithmetic(context, mapping, (func, args[0], args[1]))
955 955
956 956 @templatefunc('obsfateoperations(markers)')
957 957 def obsfateoperations(context, mapping, args):
958 958 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
959 959 if len(args) != 1:
960 960 # i18n: "obsfateoperations" is a keyword
961 961 raise error.ParseError(_("obsfateoperations expects one arguments"))
962 962
963 963 markers = evalfuncarg(context, mapping, args[0])
964 964
965 965 try:
966 966 data = obsutil.markersoperations(markers)
967 967 return templatekw.hybridlist(data, name='operation')
968 968 except (TypeError, KeyError):
969 969 # i18n: "obsfateoperations" is a keyword
970 970 errmsg = _("obsfateoperations first argument should be an iterable")
971 971 raise error.ParseError(errmsg)
972 972
973 973 @templatefunc('obsfatedate(markers)')
974 974 def obsfatedate(context, mapping, args):
975 975 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
976 976 if len(args) != 1:
977 977 # i18n: "obsfatedate" is a keyword
978 978 raise error.ParseError(_("obsfatedate expects one arguments"))
979 979
980 980 markers = evalfuncarg(context, mapping, args[0])
981 981
982 982 try:
983 983 data = obsutil.markersdates(markers)
984 984 return templatekw.hybridlist(data, name='date', fmt='%d %d')
985 985 except (TypeError, KeyError):
986 986 # i18n: "obsfatedate" is a keyword
987 987 errmsg = _("obsfatedate first argument should be an iterable")
988 988 raise error.ParseError(errmsg)
989 989
990 990 @templatefunc('obsfateusers(markers)')
991 991 def obsfateusers(context, mapping, args):
992 992 """Compute obsfate related information based on markers (EXPERIMENTAL)"""
993 993 if len(args) != 1:
994 994 # i18n: "obsfateusers" is a keyword
995 995 raise error.ParseError(_("obsfateusers expects one arguments"))
996 996
997 997 markers = evalfuncarg(context, mapping, args[0])
998 998
999 999 try:
1000 1000 data = obsutil.markersusers(markers)
1001 1001 return templatekw.hybridlist(data, name='user')
1002 1002 except (TypeError, KeyError, ValueError):
1003 1003 # i18n: "obsfateusers" is a keyword
1004 1004 msg = _("obsfateusers first argument should be an iterable of "
1005 1005 "obsmakers")
1006 1006 raise error.ParseError(msg)
1007 1007
1008 @templatefunc('obsfateverb(successors)')
1008 @templatefunc('obsfateverb(successors, markers)')
1009 1009 def obsfateverb(context, mapping, args):
1010 1010 """Compute obsfate related information based on successors (EXPERIMENTAL)"""
1011 if len(args) != 1:
1011 if len(args) != 2:
1012 1012 # i18n: "obsfateverb" is a keyword
1013 raise error.ParseError(_("obsfateverb expects one arguments"))
1013 raise error.ParseError(_("obsfateverb expects two arguments"))
1014 1014
1015 1015 successors = evalfuncarg(context, mapping, args[0])
1016 markers = evalfuncarg(context, mapping, args[1])
1016 1017
1017 1018 try:
1018 return obsutil.successorsetverb(successors)
1019 return obsutil.obsfateverb(successors, markers)
1019 1020 except TypeError:
1020 1021 # i18n: "obsfateverb" is a keyword
1021 1022 errmsg = _("obsfateverb first argument should be countable")
1022 1023 raise error.ParseError(errmsg)
1023 1024
1024 1025 @templatefunc('relpath(path)')
1025 1026 def relpath(context, mapping, args):
1026 1027 """Convert a repository-absolute path into a filesystem path relative to
1027 1028 the current working directory."""
1028 1029 if len(args) != 1:
1029 1030 # i18n: "relpath" is a keyword
1030 1031 raise error.ParseError(_("relpath expects one argument"))
1031 1032
1032 1033 repo = mapping['ctx'].repo()
1033 1034 path = evalstring(context, mapping, args[0])
1034 1035 return repo.pathto(path)
1035 1036
1036 1037 @templatefunc('revset(query[, formatargs...])')
1037 1038 def revset(context, mapping, args):
1038 1039 """Execute a revision set query. See
1039 1040 :hg:`help revset`."""
1040 1041 if not len(args) > 0:
1041 1042 # i18n: "revset" is a keyword
1042 1043 raise error.ParseError(_("revset expects one or more arguments"))
1043 1044
1044 1045 raw = evalstring(context, mapping, args[0])
1045 1046 ctx = mapping['ctx']
1046 1047 repo = ctx.repo()
1047 1048
1048 1049 def query(expr):
1049 1050 m = revsetmod.match(repo.ui, expr, repo=repo)
1050 1051 return m(repo)
1051 1052
1052 1053 if len(args) > 1:
1053 1054 formatargs = [evalfuncarg(context, mapping, a) for a in args[1:]]
1054 1055 revs = query(revsetlang.formatspec(raw, *formatargs))
1055 1056 revs = list(revs)
1056 1057 else:
1057 1058 revsetcache = mapping['cache'].setdefault("revsetcache", {})
1058 1059 if raw in revsetcache:
1059 1060 revs = revsetcache[raw]
1060 1061 else:
1061 1062 revs = query(raw)
1062 1063 revs = list(revs)
1063 1064 revsetcache[raw] = revs
1064 1065
1065 1066 return templatekw.showrevslist("revision", revs, **mapping)
1066 1067
1067 1068 @templatefunc('rstdoc(text, style)')
1068 1069 def rstdoc(context, mapping, args):
1069 1070 """Format reStructuredText."""
1070 1071 if len(args) != 2:
1071 1072 # i18n: "rstdoc" is a keyword
1072 1073 raise error.ParseError(_("rstdoc expects two arguments"))
1073 1074
1074 1075 text = evalstring(context, mapping, args[0])
1075 1076 style = evalstring(context, mapping, args[1])
1076 1077
1077 1078 return minirst.format(text, style=style, keep=['verbose'])
1078 1079
1079 1080 @templatefunc('separate(sep, args)', argspec='sep *args')
1080 1081 def separate(context, mapping, args):
1081 1082 """Add a separator between non-empty arguments."""
1082 1083 if 'sep' not in args:
1083 1084 # i18n: "separate" is a keyword
1084 1085 raise error.ParseError(_("separate expects at least one argument"))
1085 1086
1086 1087 sep = evalstring(context, mapping, args['sep'])
1087 1088 first = True
1088 1089 for arg in args['args']:
1089 1090 argstr = evalstring(context, mapping, arg)
1090 1091 if not argstr:
1091 1092 continue
1092 1093 if first:
1093 1094 first = False
1094 1095 else:
1095 1096 yield sep
1096 1097 yield argstr
1097 1098
1098 1099 @templatefunc('shortest(node, minlength=4)')
1099 1100 def shortest(context, mapping, args):
1100 1101 """Obtain the shortest representation of
1101 1102 a node."""
1102 1103 if not (1 <= len(args) <= 2):
1103 1104 # i18n: "shortest" is a keyword
1104 1105 raise error.ParseError(_("shortest() expects one or two arguments"))
1105 1106
1106 1107 node = evalstring(context, mapping, args[0])
1107 1108
1108 1109 minlength = 4
1109 1110 if len(args) > 1:
1110 1111 minlength = evalinteger(context, mapping, args[1],
1111 1112 # i18n: "shortest" is a keyword
1112 1113 _("shortest() expects an integer minlength"))
1113 1114
1114 1115 # _partialmatch() of filtered changelog could take O(len(repo)) time,
1115 1116 # which would be unacceptably slow. so we look for hash collision in
1116 1117 # unfiltered space, which means some hashes may be slightly longer.
1117 1118 cl = mapping['ctx']._repo.unfiltered().changelog
1118 1119 return cl.shortest(node, minlength)
1119 1120
1120 1121 @templatefunc('strip(text[, chars])')
1121 1122 def strip(context, mapping, args):
1122 1123 """Strip characters from a string. By default,
1123 1124 strips all leading and trailing whitespace."""
1124 1125 if not (1 <= len(args) <= 2):
1125 1126 # i18n: "strip" is a keyword
1126 1127 raise error.ParseError(_("strip expects one or two arguments"))
1127 1128
1128 1129 text = evalstring(context, mapping, args[0])
1129 1130 if len(args) == 2:
1130 1131 chars = evalstring(context, mapping, args[1])
1131 1132 return text.strip(chars)
1132 1133 return text.strip()
1133 1134
1134 1135 @templatefunc('sub(pattern, replacement, expression)')
1135 1136 def sub(context, mapping, args):
1136 1137 """Perform text substitution
1137 1138 using regular expressions."""
1138 1139 if len(args) != 3:
1139 1140 # i18n: "sub" is a keyword
1140 1141 raise error.ParseError(_("sub expects three arguments"))
1141 1142
1142 1143 pat = evalstring(context, mapping, args[0])
1143 1144 rpl = evalstring(context, mapping, args[1])
1144 1145 src = evalstring(context, mapping, args[2])
1145 1146 try:
1146 1147 patre = re.compile(pat)
1147 1148 except re.error:
1148 1149 # i18n: "sub" is a keyword
1149 1150 raise error.ParseError(_("sub got an invalid pattern: %s") % pat)
1150 1151 try:
1151 1152 yield patre.sub(rpl, src)
1152 1153 except re.error:
1153 1154 # i18n: "sub" is a keyword
1154 1155 raise error.ParseError(_("sub got an invalid replacement: %s") % rpl)
1155 1156
1156 1157 @templatefunc('startswith(pattern, text)')
1157 1158 def startswith(context, mapping, args):
1158 1159 """Returns the value from the "text" argument
1159 1160 if it begins with the content from the "pattern" argument."""
1160 1161 if len(args) != 2:
1161 1162 # i18n: "startswith" is a keyword
1162 1163 raise error.ParseError(_("startswith expects two arguments"))
1163 1164
1164 1165 patn = evalstring(context, mapping, args[0])
1165 1166 text = evalstring(context, mapping, args[1])
1166 1167 if text.startswith(patn):
1167 1168 return text
1168 1169 return ''
1169 1170
1170 1171 @templatefunc('word(number, text[, separator])')
1171 1172 def word(context, mapping, args):
1172 1173 """Return the nth word from a string."""
1173 1174 if not (2 <= len(args) <= 3):
1174 1175 # i18n: "word" is a keyword
1175 1176 raise error.ParseError(_("word expects two or three arguments, got %d")
1176 1177 % len(args))
1177 1178
1178 1179 num = evalinteger(context, mapping, args[0],
1179 1180 # i18n: "word" is a keyword
1180 1181 _("word expects an integer index"))
1181 1182 text = evalstring(context, mapping, args[1])
1182 1183 if len(args) == 3:
1183 1184 splitter = evalstring(context, mapping, args[2])
1184 1185 else:
1185 1186 splitter = None
1186 1187
1187 1188 tokens = text.split(splitter)
1188 1189 if num >= len(tokens) or num < -len(tokens):
1189 1190 return ''
1190 1191 else:
1191 1192 return tokens[num]
1192 1193
1193 1194 # methods to interpret function arguments or inner expressions (e.g. {_(x)})
1194 1195 exprmethods = {
1195 1196 "integer": lambda e, c: (runinteger, e[1]),
1196 1197 "string": lambda e, c: (runstring, e[1]),
1197 1198 "symbol": lambda e, c: (runsymbol, e[1]),
1198 1199 "template": buildtemplate,
1199 1200 "group": lambda e, c: compileexp(e[1], c, exprmethods),
1200 1201 ".": buildmember,
1201 1202 "|": buildfilter,
1202 1203 "%": buildmap,
1203 1204 "func": buildfunc,
1204 1205 "keyvalue": buildkeyvaluepair,
1205 1206 "+": lambda e, c: buildarithmetic(e, c, lambda a, b: a + b),
1206 1207 "-": lambda e, c: buildarithmetic(e, c, lambda a, b: a - b),
1207 1208 "negate": buildnegate,
1208 1209 "*": lambda e, c: buildarithmetic(e, c, lambda a, b: a * b),
1209 1210 "/": lambda e, c: buildarithmetic(e, c, lambda a, b: a // b),
1210 1211 }
1211 1212
1212 1213 # methods to interpret top-level template (e.g. {x}, {x|_}, {x % "y"})
1213 1214 methods = exprmethods.copy()
1214 1215 methods["integer"] = exprmethods["symbol"] # '{1}' as variable
1215 1216
1216 1217 class _aliasrules(parser.basealiasrules):
1217 1218 """Parsing and expansion rule set of template aliases"""
1218 1219 _section = _('template alias')
1219 1220 _parse = staticmethod(_parseexpr)
1220 1221
1221 1222 @staticmethod
1222 1223 def _trygetfunc(tree):
1223 1224 """Return (name, args) if tree is func(...) or ...|filter; otherwise
1224 1225 None"""
1225 1226 if tree[0] == 'func' and tree[1][0] == 'symbol':
1226 1227 return tree[1][1], getlist(tree[2])
1227 1228 if tree[0] == '|' and tree[2][0] == 'symbol':
1228 1229 return tree[2][1], [tree[1]]
1229 1230
1230 1231 def expandaliases(tree, aliases):
1231 1232 """Return new tree of aliases are expanded"""
1232 1233 aliasmap = _aliasrules.buildmap(aliases)
1233 1234 return _aliasrules.expand(aliasmap, tree)
1234 1235
1235 1236 # template engine
1236 1237
1237 1238 stringify = templatefilters.stringify
1238 1239
1239 1240 def _flatten(thing):
1240 1241 '''yield a single stream from a possibly nested set of iterators'''
1241 1242 thing = templatekw.unwraphybrid(thing)
1242 1243 if isinstance(thing, bytes):
1243 1244 yield thing
1244 1245 elif isinstance(thing, str):
1245 1246 # We can only hit this on Python 3, and it's here to guard
1246 1247 # against infinite recursion.
1247 1248 raise error.ProgrammingError('Mercurial IO including templates is done'
1248 1249 ' with bytes, not strings')
1249 1250 elif thing is None:
1250 1251 pass
1251 1252 elif not util.safehasattr(thing, '__iter__'):
1252 1253 yield pycompat.bytestr(thing)
1253 1254 else:
1254 1255 for i in thing:
1255 1256 i = templatekw.unwraphybrid(i)
1256 1257 if isinstance(i, bytes):
1257 1258 yield i
1258 1259 elif i is None:
1259 1260 pass
1260 1261 elif not util.safehasattr(i, '__iter__'):
1261 1262 yield pycompat.bytestr(i)
1262 1263 else:
1263 1264 for j in _flatten(i):
1264 1265 yield j
1265 1266
1266 1267 def unquotestring(s):
1267 1268 '''unwrap quotes if any; otherwise returns unmodified string'''
1268 1269 if len(s) < 2 or s[0] not in "'\"" or s[0] != s[-1]:
1269 1270 return s
1270 1271 return s[1:-1]
1271 1272
1272 1273 class engine(object):
1273 1274 '''template expansion engine.
1274 1275
1275 1276 template expansion works like this. a map file contains key=value
1276 1277 pairs. if value is quoted, it is treated as string. otherwise, it
1277 1278 is treated as name of template file.
1278 1279
1279 1280 templater is asked to expand a key in map. it looks up key, and
1280 1281 looks for strings like this: {foo}. it expands {foo} by looking up
1281 1282 foo in map, and substituting it. expansion is recursive: it stops
1282 1283 when there is no more {foo} to replace.
1283 1284
1284 1285 expansion also allows formatting and filtering.
1285 1286
1286 1287 format uses key to expand each item in list. syntax is
1287 1288 {key%format}.
1288 1289
1289 1290 filter uses function to transform value. syntax is
1290 1291 {key|filter1|filter2|...}.'''
1291 1292
1292 1293 def __init__(self, loader, filters=None, defaults=None, aliases=()):
1293 1294 self._loader = loader
1294 1295 if filters is None:
1295 1296 filters = {}
1296 1297 self._filters = filters
1297 1298 if defaults is None:
1298 1299 defaults = {}
1299 1300 self._defaults = defaults
1300 1301 self._aliasmap = _aliasrules.buildmap(aliases)
1301 1302 self._cache = {} # key: (func, data)
1302 1303
1303 1304 def _load(self, t):
1304 1305 '''load, parse, and cache a template'''
1305 1306 if t not in self._cache:
1306 1307 # put poison to cut recursion while compiling 't'
1307 1308 self._cache[t] = (_runrecursivesymbol, t)
1308 1309 try:
1309 1310 x = parse(self._loader(t))
1310 1311 if self._aliasmap:
1311 1312 x = _aliasrules.expand(self._aliasmap, x)
1312 1313 self._cache[t] = compileexp(x, self, methods)
1313 1314 except: # re-raises
1314 1315 del self._cache[t]
1315 1316 raise
1316 1317 return self._cache[t]
1317 1318
1318 1319 def process(self, t, mapping):
1319 1320 '''Perform expansion. t is name of map element to expand.
1320 1321 mapping contains added elements for use during expansion. Is a
1321 1322 generator.'''
1322 1323 func, data = self._load(t)
1323 1324 return _flatten(func(self, mapping, data))
1324 1325
1325 1326 engines = {'default': engine}
1326 1327
1327 1328 def stylelist():
1328 1329 paths = templatepaths()
1329 1330 if not paths:
1330 1331 return _('no templates found, try `hg debuginstall` for more info')
1331 1332 dirlist = os.listdir(paths[0])
1332 1333 stylelist = []
1333 1334 for file in dirlist:
1334 1335 split = file.split(".")
1335 1336 if split[-1] in ('orig', 'rej'):
1336 1337 continue
1337 1338 if split[0] == "map-cmdline":
1338 1339 stylelist.append(split[1])
1339 1340 return ", ".join(sorted(stylelist))
1340 1341
1341 1342 def _readmapfile(mapfile):
1342 1343 """Load template elements from the given map file"""
1343 1344 if not os.path.exists(mapfile):
1344 1345 raise error.Abort(_("style '%s' not found") % mapfile,
1345 1346 hint=_("available styles: %s") % stylelist())
1346 1347
1347 1348 base = os.path.dirname(mapfile)
1348 1349 conf = config.config(includepaths=templatepaths())
1349 1350 conf.read(mapfile, remap={'': 'templates'})
1350 1351
1351 1352 cache = {}
1352 1353 tmap = {}
1353 1354 aliases = []
1354 1355
1355 1356 val = conf.get('templates', '__base__')
1356 1357 if val and val[0] not in "'\"":
1357 1358 # treat as a pointer to a base class for this style
1358 1359 path = util.normpath(os.path.join(base, val))
1359 1360
1360 1361 # fallback check in template paths
1361 1362 if not os.path.exists(path):
1362 1363 for p in templatepaths():
1363 1364 p2 = util.normpath(os.path.join(p, val))
1364 1365 if os.path.isfile(p2):
1365 1366 path = p2
1366 1367 break
1367 1368 p3 = util.normpath(os.path.join(p2, "map"))
1368 1369 if os.path.isfile(p3):
1369 1370 path = p3
1370 1371 break
1371 1372
1372 1373 cache, tmap, aliases = _readmapfile(path)
1373 1374
1374 1375 for key, val in conf['templates'].items():
1375 1376 if not val:
1376 1377 raise error.ParseError(_('missing value'),
1377 1378 conf.source('templates', key))
1378 1379 if val[0] in "'\"":
1379 1380 if val[0] != val[-1]:
1380 1381 raise error.ParseError(_('unmatched quotes'),
1381 1382 conf.source('templates', key))
1382 1383 cache[key] = unquotestring(val)
1383 1384 elif key != '__base__':
1384 1385 val = 'default', val
1385 1386 if ':' in val[1]:
1386 1387 val = val[1].split(':', 1)
1387 1388 tmap[key] = val[0], os.path.join(base, val[1])
1388 1389 aliases.extend(conf['templatealias'].items())
1389 1390 return cache, tmap, aliases
1390 1391
1391 1392 class TemplateNotFound(error.Abort):
1392 1393 pass
1393 1394
1394 1395 class templater(object):
1395 1396
1396 1397 def __init__(self, filters=None, defaults=None, cache=None, aliases=(),
1397 1398 minchunk=1024, maxchunk=65536):
1398 1399 '''set up template engine.
1399 1400 filters is dict of functions. each transforms a value into another.
1400 1401 defaults is dict of default map definitions.
1401 1402 aliases is list of alias (name, replacement) pairs.
1402 1403 '''
1403 1404 if filters is None:
1404 1405 filters = {}
1405 1406 if defaults is None:
1406 1407 defaults = {}
1407 1408 if cache is None:
1408 1409 cache = {}
1409 1410 self.cache = cache.copy()
1410 1411 self.map = {}
1411 1412 self.filters = templatefilters.filters.copy()
1412 1413 self.filters.update(filters)
1413 1414 self.defaults = defaults
1414 1415 self._aliases = aliases
1415 1416 self.minchunk, self.maxchunk = minchunk, maxchunk
1416 1417 self.ecache = {}
1417 1418
1418 1419 @classmethod
1419 1420 def frommapfile(cls, mapfile, filters=None, defaults=None, cache=None,
1420 1421 minchunk=1024, maxchunk=65536):
1421 1422 """Create templater from the specified map file"""
1422 1423 t = cls(filters, defaults, cache, [], minchunk, maxchunk)
1423 1424 cache, tmap, aliases = _readmapfile(mapfile)
1424 1425 t.cache.update(cache)
1425 1426 t.map = tmap
1426 1427 t._aliases = aliases
1427 1428 return t
1428 1429
1429 1430 def __contains__(self, key):
1430 1431 return key in self.cache or key in self.map
1431 1432
1432 1433 def load(self, t):
1433 1434 '''Get the template for the given template name. Use a local cache.'''
1434 1435 if t not in self.cache:
1435 1436 try:
1436 1437 self.cache[t] = util.readfile(self.map[t][1])
1437 1438 except KeyError as inst:
1438 1439 raise TemplateNotFound(_('"%s" not in template map') %
1439 1440 inst.args[0])
1440 1441 except IOError as inst:
1441 1442 raise IOError(inst.args[0], _('template file %s: %s') %
1442 1443 (self.map[t][1], inst.args[1]))
1443 1444 return self.cache[t]
1444 1445
1445 1446 def render(self, mapping):
1446 1447 """Render the default unnamed template and return result as string"""
1447 1448 mapping = pycompat.strkwargs(mapping)
1448 1449 return stringify(self('', **mapping))
1449 1450
1450 1451 def __call__(self, t, **mapping):
1451 1452 mapping = pycompat.byteskwargs(mapping)
1452 1453 ttype = t in self.map and self.map[t][0] or 'default'
1453 1454 if ttype not in self.ecache:
1454 1455 try:
1455 1456 ecls = engines[ttype]
1456 1457 except KeyError:
1457 1458 raise error.Abort(_('invalid template engine: %s') % ttype)
1458 1459 self.ecache[ttype] = ecls(self.load, self.filters, self.defaults,
1459 1460 self._aliases)
1460 1461 proc = self.ecache[ttype]
1461 1462
1462 1463 stream = proc.process(t, mapping)
1463 1464 if self.minchunk:
1464 1465 stream = util.increasingchunks(stream, min=self.minchunk,
1465 1466 max=self.maxchunk)
1466 1467 return stream
1467 1468
1468 1469 def templatepaths():
1469 1470 '''return locations used for template files.'''
1470 1471 pathsrel = ['templates']
1471 1472 paths = [os.path.normpath(os.path.join(util.datapath, f))
1472 1473 for f in pathsrel]
1473 1474 return [p for p in paths if os.path.isdir(p)]
1474 1475
1475 1476 def templatepath(name):
1476 1477 '''return location of template file. returns None if not found.'''
1477 1478 for p in templatepaths():
1478 1479 f = os.path.join(p, name)
1479 1480 if os.path.exists(f):
1480 1481 return f
1481 1482 return None
1482 1483
1483 1484 def stylemap(styles, paths=None):
1484 1485 """Return path to mapfile for a given style.
1485 1486
1486 1487 Searches mapfile in the following locations:
1487 1488 1. templatepath/style/map
1488 1489 2. templatepath/map-style
1489 1490 3. templatepath/map
1490 1491 """
1491 1492
1492 1493 if paths is None:
1493 1494 paths = templatepaths()
1494 1495 elif isinstance(paths, str):
1495 1496 paths = [paths]
1496 1497
1497 1498 if isinstance(styles, str):
1498 1499 styles = [styles]
1499 1500
1500 1501 for style in styles:
1501 1502 # only plain name is allowed to honor template paths
1502 1503 if (not style
1503 1504 or style in (os.curdir, os.pardir)
1504 1505 or pycompat.ossep in style
1505 1506 or pycompat.osaltsep and pycompat.osaltsep in style):
1506 1507 continue
1507 1508 locations = [os.path.join(style, 'map'), 'map-' + style]
1508 1509 locations.append('map')
1509 1510
1510 1511 for path in paths:
1511 1512 for location in locations:
1512 1513 mapfile = os.path.join(path, location)
1513 1514 if os.path.isfile(mapfile):
1514 1515 return style, mapfile
1515 1516
1516 1517 raise RuntimeError("No hgweb templates found in %r" % paths)
1517 1518
1518 1519 def loadfunction(ui, extname, registrarobj):
1519 1520 """Load template function from specified registrarobj
1520 1521 """
1521 1522 for name, func in registrarobj._table.iteritems():
1522 1523 funcs[name] = func
1523 1524
1524 1525 # tell hggettext to extract docstrings from these functions:
1525 1526 i18nfunctions = funcs.values()
@@ -1,2545 +1,2545 b''
1 1 This test file test the various templates related to obsmarkers.
2 2
3 3 Global setup
4 4 ============
5 5
6 6 $ . $TESTDIR/testlib/obsmarker-common.sh
7 7 $ cat >> $HGRCPATH <<EOF
8 8 > [ui]
9 9 > interactive = true
10 10 > [phases]
11 11 > publish=False
12 12 > [experimental]
13 13 > evolution=true
14 14 > [templates]
15 15 > obsfatesuccessors = "{if(successors, " as ")}{join(successors, ", ")}"
16 > obsfateverb = "{obsfateverb(successors)}"
16 > obsfateverb = "{obsfateverb(successors, markers)}"
17 17 > obsfateoperations = "{if(obsfateoperations(markers), " using {join(obsfateoperations(markers), ", ")}")}"
18 18 > obsfateusers = "{if(obsfateusers(markers), " by {join(obsfateusers(markers), ", ")}")}"
19 19 > obsfatedate = "{if(obsfatedate(markers), "{ifeq(min(obsfatedate(markers)), max(obsfatedate(markers)), " (at {min(obsfatedate(markers))|isodate})", " (between {min(obsfatedate(markers))|isodate} and {max(obsfatedate(markers))|isodate})")}")}"
20 20 > obsfatetempl = "{obsfateverb}{obsfateoperations}{obsfatesuccessors}{obsfateusers}{obsfatedate}; "
21 21 > [alias]
22 22 > tlog = log -G -T '{node|short}\
23 23 > {if(predecessors, "\n Predecessors: {predecessors}")}\
24 24 > {if(predecessors, "\n semi-colon: {join(predecessors, "; ")}")}\
25 25 > {if(predecessors, "\n json: {predecessors|json}")}\
26 26 > {if(predecessors, "\n map: {join(predecessors % "{rev}:{node}", " ")}")}\
27 27 > {if(successorssets, "\n Successors: {successorssets}")}\
28 28 > {if(successorssets, "\n multi-line: {join(successorssets, "\n multi-line: ")}")}\
29 29 > {if(successorssets, "\n json: {successorssets|json}")}\n'
30 30 > fatelog = log -G -T '{node|short}\n{if(succsandmarkers, " Obsfate: {succsandmarkers % "{obsfatetempl}"} \n" )}'
31 31 > fatelogjson = log -G -T '{node|short}\n{if(succsandmarkers, " Obsfate: {succsandmarkers|json}\n")}'
32 32 > fatelogkw = log -G -T '{node|short}\n{if(obsfate, "{obsfate % " Obsfate: {fate}\n"}")}'
33 33 > EOF
34 34
35 35 Test templates on amended commit
36 36 ================================
37 37
38 38 Test setup
39 39 ----------
40 40
41 41 $ hg init $TESTTMP/templates-local-amend
42 42 $ cd $TESTTMP/templates-local-amend
43 43 $ mkcommit ROOT
44 44 $ mkcommit A0
45 45 $ echo 42 >> A0
46 46 $ hg commit --amend -m "A1" --config devel.default-date="1234567890 0"
47 47 $ hg commit --amend -m "A2" --config devel.default-date="987654321 0" --config devel.user.obsmarker=test2
48 48
49 49 $ hg log --hidden -G
50 50 @ changeset: 3:d004c8f274b9
51 51 | tag: tip
52 52 | parent: 0:ea207398892e
53 53 | user: test
54 54 | date: Thu Jan 01 00:00:00 1970 +0000
55 55 | summary: A2
56 56 |
57 57 | x changeset: 2:a468dc9b3633
58 58 |/ parent: 0:ea207398892e
59 59 | user: test
60 60 | date: Thu Jan 01 00:00:00 1970 +0000
61 61 | obsolete: rewritten using amend as 3:d004c8f274b9 by test2
62 62 | summary: A1
63 63 |
64 64 | x changeset: 1:471f378eab4c
65 65 |/ user: test
66 66 | date: Thu Jan 01 00:00:00 1970 +0000
67 67 | obsolete: rewritten using amend as 2:a468dc9b3633
68 68 | summary: A0
69 69 |
70 70 o changeset: 0:ea207398892e
71 71 user: test
72 72 date: Thu Jan 01 00:00:00 1970 +0000
73 73 summary: ROOT
74 74
75 75 Check templates
76 76 ---------------
77 77 $ hg up 'desc(A0)' --hidden
78 78 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
79 79
80 80 Predecessors template should show current revision as it is the working copy
81 81 $ hg tlog
82 82 o d004c8f274b9
83 83 | Predecessors: 1:471f378eab4c
84 84 | semi-colon: 1:471f378eab4c
85 85 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
86 86 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
87 87 | @ 471f378eab4c
88 88 |/ Successors: 3:d004c8f274b9
89 89 | multi-line: 3:d004c8f274b9
90 90 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
91 91 o ea207398892e
92 92
93 93 $ hg fatelog
94 94 o d004c8f274b9
95 95 |
96 96 | @ 471f378eab4c
97 97 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test, test2 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000);
98 98 o ea207398892e
99 99
100 100
101 101 $ hg fatelogkw
102 102 o d004c8f274b9
103 103 |
104 104 | @ 471f378eab4c
105 105 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test, test2
106 106 o ea207398892e
107 107
108 108
109 109 $ hg log -G --config ui.logtemplate=
110 110 o changeset: 3:d004c8f274b9
111 111 | tag: tip
112 112 | parent: 0:ea207398892e
113 113 | user: test
114 114 | date: Thu Jan 01 00:00:00 1970 +0000
115 115 | summary: A2
116 116 |
117 117 | @ changeset: 1:471f378eab4c
118 118 |/ user: test
119 119 | date: Thu Jan 01 00:00:00 1970 +0000
120 120 | obsolete: rewritten using amend as 3:d004c8f274b9 by test, test2
121 121 | summary: A0
122 122 |
123 123 o changeset: 0:ea207398892e
124 124 user: test
125 125 date: Thu Jan 01 00:00:00 1970 +0000
126 126 summary: ROOT
127 127
128 128
129 129 $ hg log -G -T "default"
130 130 o changeset: 3:d004c8f274b9
131 131 | tag: tip
132 132 | parent: 0:ea207398892e
133 133 | user: test
134 134 | date: Thu Jan 01 00:00:00 1970 +0000
135 135 | summary: A2
136 136 |
137 137 | @ changeset: 1:471f378eab4c
138 138 |/ user: test
139 139 | date: Thu Jan 01 00:00:00 1970 +0000
140 140 | obsolete: rewritten using amend as 3:d004c8f274b9 by test, test2
141 141 | summary: A0
142 142 |
143 143 o changeset: 0:ea207398892e
144 144 user: test
145 145 date: Thu Jan 01 00:00:00 1970 +0000
146 146 summary: ROOT
147 147
148 148 $ hg up 'desc(A1)' --hidden
149 149 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
150 150
151 151 Predecessors template should show current revision as it is the working copy
152 152 $ hg tlog
153 153 o d004c8f274b9
154 154 | Predecessors: 2:a468dc9b3633
155 155 | semi-colon: 2:a468dc9b3633
156 156 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
157 157 | map: 2:a468dc9b36338b14fdb7825f55ce3df4e71517ad
158 158 | @ a468dc9b3633
159 159 |/ Successors: 3:d004c8f274b9
160 160 | multi-line: 3:d004c8f274b9
161 161 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
162 162 o ea207398892e
163 163
164 164 $ hg fatelog
165 165 o d004c8f274b9
166 166 |
167 167 | @ a468dc9b3633
168 168 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
169 169 o ea207398892e
170 170
171 171 Predecessors template should show all the predecessors as we force their display
172 172 with --hidden
173 173 $ hg tlog --hidden
174 174 o d004c8f274b9
175 175 | Predecessors: 2:a468dc9b3633
176 176 | semi-colon: 2:a468dc9b3633
177 177 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
178 178 | map: 2:a468dc9b36338b14fdb7825f55ce3df4e71517ad
179 179 | @ a468dc9b3633
180 180 |/ Predecessors: 1:471f378eab4c
181 181 | semi-colon: 1:471f378eab4c
182 182 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
183 183 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
184 184 | Successors: 3:d004c8f274b9
185 185 | multi-line: 3:d004c8f274b9
186 186 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
187 187 | x 471f378eab4c
188 188 |/ Successors: 2:a468dc9b3633
189 189 | multi-line: 2:a468dc9b3633
190 190 | json: [["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]]
191 191 o ea207398892e
192 192
193 193 $ hg fatelog --hidden
194 194 o d004c8f274b9
195 195 |
196 196 | @ a468dc9b3633
197 197 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
198 198 | x 471f378eab4c
199 199 |/ Obsfate: rewritten using amend as 2:a468dc9b3633 by test (at 2009-02-13 23:31 +0000);
200 200 o ea207398892e
201 201
202 202
203 203 Predecessors template shouldn't show anything as all obsolete commit are not
204 204 visible.
205 205 $ hg up 'desc(A2)'
206 206 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
207 207 $ hg tlog
208 208 @ d004c8f274b9
209 209 |
210 210 o ea207398892e
211 211
212 212 $ hg tlog --hidden
213 213 @ d004c8f274b9
214 214 | Predecessors: 2:a468dc9b3633
215 215 | semi-colon: 2:a468dc9b3633
216 216 | json: ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]
217 217 | map: 2:a468dc9b36338b14fdb7825f55ce3df4e71517ad
218 218 | x a468dc9b3633
219 219 |/ Predecessors: 1:471f378eab4c
220 220 | semi-colon: 1:471f378eab4c
221 221 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
222 222 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
223 223 | Successors: 3:d004c8f274b9
224 224 | multi-line: 3:d004c8f274b9
225 225 | json: [["d004c8f274b9ec480a47a93c10dac5eee63adb78"]]
226 226 | x 471f378eab4c
227 227 |/ Successors: 2:a468dc9b3633
228 228 | multi-line: 2:a468dc9b3633
229 229 | json: [["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]]
230 230 o ea207398892e
231 231
232 232 $ hg fatelog
233 233 @ d004c8f274b9
234 234 |
235 235 o ea207398892e
236 236
237 237
238 238 $ hg fatelog --hidden
239 239 @ d004c8f274b9
240 240 |
241 241 | x a468dc9b3633
242 242 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
243 243 | x 471f378eab4c
244 244 |/ Obsfate: rewritten using amend as 2:a468dc9b3633 by test (at 2009-02-13 23:31 +0000);
245 245 o ea207398892e
246 246
247 247 $ hg fatelogjson --hidden
248 248 @ d004c8f274b9
249 249 |
250 250 | x a468dc9b3633
251 251 |/ Obsfate: [{"markers": [["a468dc9b36338b14fdb7825f55ce3df4e71517ad", ["d004c8f274b9ec480a47a93c10dac5eee63adb78"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test2"]], [987654321.0, 0], null]], "successors": ["d004c8f274b9ec480a47a93c10dac5eee63adb78"]}]
252 252 | x 471f378eab4c
253 253 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"], 0, [["ef1", "9"], ["operation", "amend"], ["user", "test"]], [1234567890.0, 0], null]], "successors": ["a468dc9b36338b14fdb7825f55ce3df4e71517ad"]}]
254 254 o ea207398892e
255 255
256 256
257 257 Check other fatelog implementations
258 258 -----------------------------------
259 259
260 260 $ hg fatelogkw --hidden -q
261 261 @ d004c8f274b9
262 262 |
263 263 | x a468dc9b3633
264 264 |/ Obsfate: rewritten using amend as 3:d004c8f274b9
265 265 | x 471f378eab4c
266 266 |/ Obsfate: rewritten using amend as 2:a468dc9b3633
267 267 o ea207398892e
268 268
269 269 $ hg fatelogkw --hidden
270 270 @ d004c8f274b9
271 271 |
272 272 | x a468dc9b3633
273 273 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2
274 274 | x 471f378eab4c
275 275 |/ Obsfate: rewritten using amend as 2:a468dc9b3633
276 276 o ea207398892e
277 277
278 278 $ hg fatelogkw --hidden -v
279 279 @ d004c8f274b9
280 280 |
281 281 | x a468dc9b3633
282 282 |/ Obsfate: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000)
283 283 | x 471f378eab4c
284 284 |/ Obsfate: rewritten using amend as 2:a468dc9b3633 by test (at 2009-02-13 23:31 +0000)
285 285 o ea207398892e
286 286
287 287
288 288 $ hg log -G -T "default" --hidden
289 289 @ changeset: 3:d004c8f274b9
290 290 | tag: tip
291 291 | parent: 0:ea207398892e
292 292 | user: test
293 293 | date: Thu Jan 01 00:00:00 1970 +0000
294 294 | summary: A2
295 295 |
296 296 | x changeset: 2:a468dc9b3633
297 297 |/ parent: 0:ea207398892e
298 298 | user: test
299 299 | date: Thu Jan 01 00:00:00 1970 +0000
300 300 | obsolete: rewritten using amend as 3:d004c8f274b9 by test2
301 301 | summary: A1
302 302 |
303 303 | x changeset: 1:471f378eab4c
304 304 |/ user: test
305 305 | date: Thu Jan 01 00:00:00 1970 +0000
306 306 | obsolete: rewritten using amend as 2:a468dc9b3633
307 307 | summary: A0
308 308 |
309 309 o changeset: 0:ea207398892e
310 310 user: test
311 311 date: Thu Jan 01 00:00:00 1970 +0000
312 312 summary: ROOT
313 313
314 314 $ hg log -G -T "default" --hidden -v
315 315 @ changeset: 3:d004c8f274b9
316 316 | tag: tip
317 317 | parent: 0:ea207398892e
318 318 | user: test
319 319 | date: Thu Jan 01 00:00:00 1970 +0000
320 320 | files: A0
321 321 | description:
322 322 | A2
323 323 |
324 324 |
325 325 | x changeset: 2:a468dc9b3633
326 326 |/ parent: 0:ea207398892e
327 327 | user: test
328 328 | date: Thu Jan 01 00:00:00 1970 +0000
329 329 | obsolete: rewritten using amend as 3:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000)
330 330 | files: A0
331 331 | description:
332 332 | A1
333 333 |
334 334 |
335 335 | x changeset: 1:471f378eab4c
336 336 |/ user: test
337 337 | date: Thu Jan 01 00:00:00 1970 +0000
338 338 | obsolete: rewritten using amend as 2:a468dc9b3633 by test (at 2009-02-13 23:31 +0000)
339 339 | files: A0
340 340 | description:
341 341 | A0
342 342 |
343 343 |
344 344 o changeset: 0:ea207398892e
345 345 user: test
346 346 date: Thu Jan 01 00:00:00 1970 +0000
347 347 files: ROOT
348 348 description:
349 349 ROOT
350 350
351 351
352 352 Test templates with splitted commit
353 353 ===================================
354 354
355 355 $ hg init $TESTTMP/templates-local-split
356 356 $ cd $TESTTMP/templates-local-split
357 357 $ mkcommit ROOT
358 358 $ echo 42 >> a
359 359 $ echo 43 >> b
360 360 $ hg commit -A -m "A0"
361 361 adding a
362 362 adding b
363 363 $ hg log --hidden -G
364 364 @ changeset: 1:471597cad322
365 365 | tag: tip
366 366 | user: test
367 367 | date: Thu Jan 01 00:00:00 1970 +0000
368 368 | summary: A0
369 369 |
370 370 o changeset: 0:ea207398892e
371 371 user: test
372 372 date: Thu Jan 01 00:00:00 1970 +0000
373 373 summary: ROOT
374 374
375 375 # Simulate split
376 376 $ hg up -r "desc(ROOT)"
377 377 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
378 378 $ echo 42 >> a
379 379 $ hg commit -A -m "A0"
380 380 adding a
381 381 created new head
382 382 $ echo 43 >> b
383 383 $ hg commit -A -m "A0"
384 384 adding b
385 385 $ hg debugobsolete `getid "1"` `getid "2"` `getid "3"`
386 386 obsoleted 1 changesets
387 387
388 388 $ hg log --hidden -G
389 389 @ changeset: 3:f257fde29c7a
390 390 | tag: tip
391 391 | user: test
392 392 | date: Thu Jan 01 00:00:00 1970 +0000
393 393 | summary: A0
394 394 |
395 395 o changeset: 2:337fec4d2edc
396 396 | parent: 0:ea207398892e
397 397 | user: test
398 398 | date: Thu Jan 01 00:00:00 1970 +0000
399 399 | summary: A0
400 400 |
401 401 | x changeset: 1:471597cad322
402 402 |/ user: test
403 403 | date: Thu Jan 01 00:00:00 1970 +0000
404 404 | obsolete: split as 2:337fec4d2edc, 3:f257fde29c7a
405 405 | summary: A0
406 406 |
407 407 o changeset: 0:ea207398892e
408 408 user: test
409 409 date: Thu Jan 01 00:00:00 1970 +0000
410 410 summary: ROOT
411 411
412 412 Check templates
413 413 ---------------
414 414
415 415 $ hg up 'obsolete()' --hidden
416 416 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
417 417
418 418 Predecessors template should show current revision as it is the working copy
419 419 $ hg tlog
420 420 o f257fde29c7a
421 421 | Predecessors: 1:471597cad322
422 422 | semi-colon: 1:471597cad322
423 423 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
424 424 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
425 425 o 337fec4d2edc
426 426 | Predecessors: 1:471597cad322
427 427 | semi-colon: 1:471597cad322
428 428 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
429 429 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
430 430 | @ 471597cad322
431 431 |/ Successors: 2:337fec4d2edc 3:f257fde29c7a
432 432 | multi-line: 2:337fec4d2edc 3:f257fde29c7a
433 433 | json: [["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]]
434 434 o ea207398892e
435 435
436 436
437 437 $ hg fatelog
438 438 o f257fde29c7a
439 439 |
440 440 o 337fec4d2edc
441 441 |
442 442 | @ 471597cad322
443 443 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000);
444 444 o ea207398892e
445 445
446 446 $ hg up f257fde29c7a
447 447 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
448 448
449 449 Predecessors template should not show a predecessor as it's not displayed in
450 450 the log
451 451 $ hg tlog
452 452 @ f257fde29c7a
453 453 |
454 454 o 337fec4d2edc
455 455 |
456 456 o ea207398892e
457 457
458 458 Predecessors template should show both predecessors as we force their display
459 459 with --hidden
460 460 $ hg tlog --hidden
461 461 @ f257fde29c7a
462 462 | Predecessors: 1:471597cad322
463 463 | semi-colon: 1:471597cad322
464 464 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
465 465 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
466 466 o 337fec4d2edc
467 467 | Predecessors: 1:471597cad322
468 468 | semi-colon: 1:471597cad322
469 469 | json: ["471597cad322d1f659bb169751be9133dad92ef3"]
470 470 | map: 1:471597cad322d1f659bb169751be9133dad92ef3
471 471 | x 471597cad322
472 472 |/ Successors: 2:337fec4d2edc 3:f257fde29c7a
473 473 | multi-line: 2:337fec4d2edc 3:f257fde29c7a
474 474 | json: [["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]]
475 475 o ea207398892e
476 476
477 477
478 478 $ hg fatelog --hidden
479 479 @ f257fde29c7a
480 480 |
481 481 o 337fec4d2edc
482 482 |
483 483 | x 471597cad322
484 484 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000);
485 485 o ea207398892e
486 486
487 487 $ hg fatelogjson --hidden
488 488 @ f257fde29c7a
489 489 |
490 490 o 337fec4d2edc
491 491 |
492 492 | x 471597cad322
493 493 |/ Obsfate: [{"markers": [["471597cad322d1f659bb169751be9133dad92ef3", ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["337fec4d2edcf0e7a467e35f818234bc620068b5", "f257fde29c7a847c9b607f6e958656d0df0fb15c"]}]
494 494 o ea207398892e
495 495
496 496 Check other fatelog implementations
497 497 -----------------------------------
498 498
499 499 $ hg fatelogkw --hidden -q
500 500 @ f257fde29c7a
501 501 |
502 502 o 337fec4d2edc
503 503 |
504 504 | x 471597cad322
505 505 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a
506 506 o ea207398892e
507 507
508 508 $ hg fatelogkw --hidden
509 509 @ f257fde29c7a
510 510 |
511 511 o 337fec4d2edc
512 512 |
513 513 | x 471597cad322
514 514 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a
515 515 o ea207398892e
516 516
517 517 $ hg fatelogkw --hidden -v
518 518 @ f257fde29c7a
519 519 |
520 520 o 337fec4d2edc
521 521 |
522 522 | x 471597cad322
523 523 |/ Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000)
524 524 o ea207398892e
525 525
526 526
527 527 $ hg log -G -T "default" --hidden
528 528 @ changeset: 3:f257fde29c7a
529 529 | tag: tip
530 530 | user: test
531 531 | date: Thu Jan 01 00:00:00 1970 +0000
532 532 | summary: A0
533 533 |
534 534 o changeset: 2:337fec4d2edc
535 535 | parent: 0:ea207398892e
536 536 | user: test
537 537 | date: Thu Jan 01 00:00:00 1970 +0000
538 538 | summary: A0
539 539 |
540 540 | x changeset: 1:471597cad322
541 541 |/ user: test
542 542 | date: Thu Jan 01 00:00:00 1970 +0000
543 543 | obsolete: split as 2:337fec4d2edc, 3:f257fde29c7a
544 544 | summary: A0
545 545 |
546 546 o changeset: 0:ea207398892e
547 547 user: test
548 548 date: Thu Jan 01 00:00:00 1970 +0000
549 549 summary: ROOT
550 550
551 551
552 552 Test templates with folded commit
553 553 =================================
554 554
555 555 Test setup
556 556 ----------
557 557
558 558 $ hg init $TESTTMP/templates-local-fold
559 559 $ cd $TESTTMP/templates-local-fold
560 560 $ mkcommit ROOT
561 561 $ mkcommit A0
562 562 $ mkcommit B0
563 563 $ hg log --hidden -G
564 564 @ changeset: 2:0dec01379d3b
565 565 | tag: tip
566 566 | user: test
567 567 | date: Thu Jan 01 00:00:00 1970 +0000
568 568 | summary: B0
569 569 |
570 570 o changeset: 1:471f378eab4c
571 571 | user: test
572 572 | date: Thu Jan 01 00:00:00 1970 +0000
573 573 | summary: A0
574 574 |
575 575 o changeset: 0:ea207398892e
576 576 user: test
577 577 date: Thu Jan 01 00:00:00 1970 +0000
578 578 summary: ROOT
579 579
580 580 Simulate a fold
581 581 $ hg up -r "desc(ROOT)"
582 582 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
583 583 $ echo "A0" > A0
584 584 $ echo "B0" > B0
585 585 $ hg commit -A -m "C0"
586 586 adding A0
587 587 adding B0
588 588 created new head
589 589 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(C0)"`
590 590 obsoleted 1 changesets
591 591 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(C0)"`
592 592 obsoleted 1 changesets
593 593
594 594 $ hg log --hidden -G
595 595 @ changeset: 3:eb5a0daa2192
596 596 | tag: tip
597 597 | parent: 0:ea207398892e
598 598 | user: test
599 599 | date: Thu Jan 01 00:00:00 1970 +0000
600 600 | summary: C0
601 601 |
602 602 | x changeset: 2:0dec01379d3b
603 603 | | user: test
604 604 | | date: Thu Jan 01 00:00:00 1970 +0000
605 605 | | obsolete: rewritten as 3:eb5a0daa2192
606 606 | | summary: B0
607 607 | |
608 608 | x changeset: 1:471f378eab4c
609 609 |/ user: test
610 610 | date: Thu Jan 01 00:00:00 1970 +0000
611 611 | obsolete: rewritten as 3:eb5a0daa2192
612 612 | summary: A0
613 613 |
614 614 o changeset: 0:ea207398892e
615 615 user: test
616 616 date: Thu Jan 01 00:00:00 1970 +0000
617 617 summary: ROOT
618 618
619 619 Check templates
620 620 ---------------
621 621
622 622 $ hg up 'desc(A0)' --hidden
623 623 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
624 624
625 625 Predecessors template should show current revision as it is the working copy
626 626 $ hg tlog
627 627 o eb5a0daa2192
628 628 | Predecessors: 1:471f378eab4c
629 629 | semi-colon: 1:471f378eab4c
630 630 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
631 631 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
632 632 | @ 471f378eab4c
633 633 |/ Successors: 3:eb5a0daa2192
634 634 | multi-line: 3:eb5a0daa2192
635 635 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
636 636 o ea207398892e
637 637
638 638
639 639 $ hg fatelog
640 640 o eb5a0daa2192
641 641 |
642 642 | @ 471f378eab4c
643 643 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
644 644 o ea207398892e
645 645
646 646 $ hg up 'desc(B0)' --hidden
647 647 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
648 648
649 649 Predecessors template should show both predecessors as they should be both
650 650 displayed
651 651 $ hg tlog
652 652 o eb5a0daa2192
653 653 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
654 654 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
655 655 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
656 656 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
657 657 | @ 0dec01379d3b
658 658 | | Successors: 3:eb5a0daa2192
659 659 | | multi-line: 3:eb5a0daa2192
660 660 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
661 661 | x 471f378eab4c
662 662 |/ Successors: 3:eb5a0daa2192
663 663 | multi-line: 3:eb5a0daa2192
664 664 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
665 665 o ea207398892e
666 666
667 667
668 668 $ hg fatelog
669 669 o eb5a0daa2192
670 670 |
671 671 | @ 0dec01379d3b
672 672 | | Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
673 673 | x 471f378eab4c
674 674 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
675 675 o ea207398892e
676 676
677 677 $ hg up 'desc(C0)'
678 678 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
679 679
680 680 Predecessors template should not show predecessors as they are not displayed in
681 681 the log
682 682 $ hg tlog
683 683 @ eb5a0daa2192
684 684 |
685 685 o ea207398892e
686 686
687 687 Predecessors template should show both predecessors as we force their display
688 688 with --hidden
689 689 $ hg tlog --hidden
690 690 @ eb5a0daa2192
691 691 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
692 692 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
693 693 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
694 694 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
695 695 | x 0dec01379d3b
696 696 | | Successors: 3:eb5a0daa2192
697 697 | | multi-line: 3:eb5a0daa2192
698 698 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
699 699 | x 471f378eab4c
700 700 |/ Successors: 3:eb5a0daa2192
701 701 | multi-line: 3:eb5a0daa2192
702 702 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
703 703 o ea207398892e
704 704
705 705
706 706 $ hg fatelog --hidden
707 707 @ eb5a0daa2192
708 708 |
709 709 | x 0dec01379d3b
710 710 | | Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
711 711 | x 471f378eab4c
712 712 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
713 713 o ea207398892e
714 714
715 715
716 716 $ hg fatelogjson --hidden
717 717 @ eb5a0daa2192
718 718 |
719 719 | x 0dec01379d3b
720 720 | | Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
721 721 | x 471f378eab4c
722 722 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
723 723 o ea207398892e
724 724
725 725 Check other fatelog implementations
726 726 -----------------------------------
727 727
728 728 $ hg fatelogkw --hidden -q
729 729 @ eb5a0daa2192
730 730 |
731 731 | x 0dec01379d3b
732 732 | | Obsfate: rewritten as 3:eb5a0daa2192
733 733 | x 471f378eab4c
734 734 |/ Obsfate: rewritten as 3:eb5a0daa2192
735 735 o ea207398892e
736 736
737 737 $ hg fatelogkw --hidden
738 738 @ eb5a0daa2192
739 739 |
740 740 | x 0dec01379d3b
741 741 | | Obsfate: rewritten as 3:eb5a0daa2192
742 742 | x 471f378eab4c
743 743 |/ Obsfate: rewritten as 3:eb5a0daa2192
744 744 o ea207398892e
745 745
746 746 $ hg fatelogkw --hidden -v
747 747 @ eb5a0daa2192
748 748 |
749 749 | x 0dec01379d3b
750 750 | | Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000)
751 751 | x 471f378eab4c
752 752 |/ Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000)
753 753 o ea207398892e
754 754
755 755 $ hg log -G -T "default" --hidden
756 756 @ changeset: 3:eb5a0daa2192
757 757 | tag: tip
758 758 | parent: 0:ea207398892e
759 759 | user: test
760 760 | date: Thu Jan 01 00:00:00 1970 +0000
761 761 | summary: C0
762 762 |
763 763 | x changeset: 2:0dec01379d3b
764 764 | | user: test
765 765 | | date: Thu Jan 01 00:00:00 1970 +0000
766 766 | | obsolete: rewritten as 3:eb5a0daa2192
767 767 | | summary: B0
768 768 | |
769 769 | x changeset: 1:471f378eab4c
770 770 |/ user: test
771 771 | date: Thu Jan 01 00:00:00 1970 +0000
772 772 | obsolete: rewritten as 3:eb5a0daa2192
773 773 | summary: A0
774 774 |
775 775 o changeset: 0:ea207398892e
776 776 user: test
777 777 date: Thu Jan 01 00:00:00 1970 +0000
778 778 summary: ROOT
779 779
780 780
781 781 Test templates with divergence
782 782 ==============================
783 783
784 784 Test setup
785 785 ----------
786 786
787 787 $ hg init $TESTTMP/templates-local-divergence
788 788 $ cd $TESTTMP/templates-local-divergence
789 789 $ mkcommit ROOT
790 790 $ mkcommit A0
791 791 $ hg commit --amend -m "A1"
792 792 $ hg log --hidden -G
793 793 @ changeset: 2:fdf9bde5129a
794 794 | tag: tip
795 795 | parent: 0:ea207398892e
796 796 | user: test
797 797 | date: Thu Jan 01 00:00:00 1970 +0000
798 798 | summary: A1
799 799 |
800 800 | x changeset: 1:471f378eab4c
801 801 |/ user: test
802 802 | date: Thu Jan 01 00:00:00 1970 +0000
803 803 | obsolete: rewritten using amend as 2:fdf9bde5129a
804 804 | summary: A0
805 805 |
806 806 o changeset: 0:ea207398892e
807 807 user: test
808 808 date: Thu Jan 01 00:00:00 1970 +0000
809 809 summary: ROOT
810 810
811 811 $ hg update --hidden 'desc(A0)'
812 812 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
813 813 $ hg commit --amend -m "A2"
814 814 $ hg log --hidden -G
815 815 @ changeset: 3:65b757b745b9
816 816 | tag: tip
817 817 | parent: 0:ea207398892e
818 818 | user: test
819 819 | date: Thu Jan 01 00:00:00 1970 +0000
820 820 | instability: content-divergent
821 821 | summary: A2
822 822 |
823 823 | o changeset: 2:fdf9bde5129a
824 824 |/ parent: 0:ea207398892e
825 825 | user: test
826 826 | date: Thu Jan 01 00:00:00 1970 +0000
827 827 | instability: content-divergent
828 828 | summary: A1
829 829 |
830 830 | x changeset: 1:471f378eab4c
831 831 |/ user: test
832 832 | date: Thu Jan 01 00:00:00 1970 +0000
833 833 | obsolete: rewritten using amend as 2:fdf9bde5129a
834 834 | obsolete: rewritten using amend as 3:65b757b745b9
835 835 | summary: A0
836 836 |
837 837 o changeset: 0:ea207398892e
838 838 user: test
839 839 date: Thu Jan 01 00:00:00 1970 +0000
840 840 summary: ROOT
841 841
842 842 $ hg commit --amend -m 'A3'
843 843 $ hg log --hidden -G
844 844 @ changeset: 4:019fadeab383
845 845 | tag: tip
846 846 | parent: 0:ea207398892e
847 847 | user: test
848 848 | date: Thu Jan 01 00:00:00 1970 +0000
849 849 | instability: content-divergent
850 850 | summary: A3
851 851 |
852 852 | x changeset: 3:65b757b745b9
853 853 |/ parent: 0:ea207398892e
854 854 | user: test
855 855 | date: Thu Jan 01 00:00:00 1970 +0000
856 856 | obsolete: rewritten using amend as 4:019fadeab383
857 857 | summary: A2
858 858 |
859 859 | o changeset: 2:fdf9bde5129a
860 860 |/ parent: 0:ea207398892e
861 861 | user: test
862 862 | date: Thu Jan 01 00:00:00 1970 +0000
863 863 | instability: content-divergent
864 864 | summary: A1
865 865 |
866 866 | x changeset: 1:471f378eab4c
867 867 |/ user: test
868 868 | date: Thu Jan 01 00:00:00 1970 +0000
869 869 | obsolete: rewritten using amend as 2:fdf9bde5129a
870 870 | obsolete: rewritten using amend as 3:65b757b745b9
871 871 | summary: A0
872 872 |
873 873 o changeset: 0:ea207398892e
874 874 user: test
875 875 date: Thu Jan 01 00:00:00 1970 +0000
876 876 summary: ROOT
877 877
878 878
879 879 Check templates
880 880 ---------------
881 881
882 882 $ hg up 'desc(A0)' --hidden
883 883 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
884 884
885 885 Predecessors template should show current revision as it is the working copy
886 886 $ hg tlog
887 887 o 019fadeab383
888 888 | Predecessors: 1:471f378eab4c
889 889 | semi-colon: 1:471f378eab4c
890 890 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
891 891 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
892 892 | o fdf9bde5129a
893 893 |/ Predecessors: 1:471f378eab4c
894 894 | semi-colon: 1:471f378eab4c
895 895 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
896 896 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
897 897 | @ 471f378eab4c
898 898 |/ Successors: 2:fdf9bde5129a; 4:019fadeab383
899 899 | multi-line: 2:fdf9bde5129a
900 900 | multi-line: 4:019fadeab383
901 901 | json: [["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]]
902 902 o ea207398892e
903 903
904 904 $ hg fatelog
905 905 o 019fadeab383
906 906 |
907 907 | o fdf9bde5129a
908 908 |/
909 909 | @ 471f378eab4c
910 910 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten using amend as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000);
911 911 o ea207398892e
912 912
913 913 $ hg up 'desc(A1)'
914 914 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
915 915
916 916 Predecessors template should not show predecessors as they are not displayed in
917 917 the log
918 918 $ hg tlog
919 919 o 019fadeab383
920 920 |
921 921 | @ fdf9bde5129a
922 922 |/
923 923 o ea207398892e
924 924
925 925
926 926 $ hg fatelog
927 927 o 019fadeab383
928 928 |
929 929 | @ fdf9bde5129a
930 930 |/
931 931 o ea207398892e
932 932
933 933 Predecessors template should the predecessors as we force their display with
934 934 --hidden
935 935 $ hg tlog --hidden
936 936 o 019fadeab383
937 937 | Predecessors: 3:65b757b745b9
938 938 | semi-colon: 3:65b757b745b9
939 939 | json: ["65b757b745b935093c87a2bccd877521cccffcbd"]
940 940 | map: 3:65b757b745b935093c87a2bccd877521cccffcbd
941 941 | x 65b757b745b9
942 942 |/ Predecessors: 1:471f378eab4c
943 943 | semi-colon: 1:471f378eab4c
944 944 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
945 945 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
946 946 | Successors: 4:019fadeab383
947 947 | multi-line: 4:019fadeab383
948 948 | json: [["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]]
949 949 | @ fdf9bde5129a
950 950 |/ Predecessors: 1:471f378eab4c
951 951 | semi-colon: 1:471f378eab4c
952 952 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
953 953 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
954 954 | x 471f378eab4c
955 955 |/ Successors: 2:fdf9bde5129a; 3:65b757b745b9
956 956 | multi-line: 2:fdf9bde5129a
957 957 | multi-line: 3:65b757b745b9
958 958 | json: [["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], ["65b757b745b935093c87a2bccd877521cccffcbd"]]
959 959 o ea207398892e
960 960
961 961
962 962 $ hg fatelog --hidden
963 963 o 019fadeab383
964 964 |
965 965 | x 65b757b745b9
966 966 |/ Obsfate: rewritten using amend as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000);
967 967 | @ fdf9bde5129a
968 968 |/
969 969 | x 471f378eab4c
970 970 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten using amend as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000);
971 971 o ea207398892e
972 972
973 973
974 974 $ hg fatelogjson --hidden
975 975 o 019fadeab383
976 976 |
977 977 | x 65b757b745b9
978 978 |/ Obsfate: [{"markers": [["65b757b745b935093c87a2bccd877521cccffcbd", ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["019fadeab383f6699fa83ad7bdb4d82ed2c0e5ab"]}]
979 979 | @ fdf9bde5129a
980 980 |/
981 981 | x 471f378eab4c
982 982 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e"]}, {"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["65b757b745b935093c87a2bccd877521cccffcbd"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["65b757b745b935093c87a2bccd877521cccffcbd"]}]
983 983 o ea207398892e
984 984
985 985
986 986 Check other fatelog implementations
987 987 -----------------------------------
988 988
989 989 $ hg fatelogkw --hidden -q
990 990 o 019fadeab383
991 991 |
992 992 | x 65b757b745b9
993 993 |/ Obsfate: rewritten using amend as 4:019fadeab383
994 994 | @ fdf9bde5129a
995 995 |/
996 996 | x 471f378eab4c
997 997 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a
998 998 | Obsfate: rewritten using amend as 3:65b757b745b9
999 999 o ea207398892e
1000 1000
1001 1001 $ hg fatelogkw --hidden
1002 1002 o 019fadeab383
1003 1003 |
1004 1004 | x 65b757b745b9
1005 1005 |/ Obsfate: rewritten using amend as 4:019fadeab383
1006 1006 | @ fdf9bde5129a
1007 1007 |/
1008 1008 | x 471f378eab4c
1009 1009 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a
1010 1010 | Obsfate: rewritten using amend as 3:65b757b745b9
1011 1011 o ea207398892e
1012 1012
1013 1013 $ hg fatelogkw --hidden -v
1014 1014 o 019fadeab383
1015 1015 |
1016 1016 | x 65b757b745b9
1017 1017 |/ Obsfate: rewritten using amend as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000)
1018 1018 | @ fdf9bde5129a
1019 1019 |/
1020 1020 | x 471f378eab4c
1021 1021 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000)
1022 1022 | Obsfate: rewritten using amend as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000)
1023 1023 o ea207398892e
1024 1024
1025 1025 $ hg log -G -T "default" --hidden
1026 1026 o changeset: 4:019fadeab383
1027 1027 | tag: tip
1028 1028 | parent: 0:ea207398892e
1029 1029 | user: test
1030 1030 | date: Thu Jan 01 00:00:00 1970 +0000
1031 1031 | instability: content-divergent
1032 1032 | summary: A3
1033 1033 |
1034 1034 | x changeset: 3:65b757b745b9
1035 1035 |/ parent: 0:ea207398892e
1036 1036 | user: test
1037 1037 | date: Thu Jan 01 00:00:00 1970 +0000
1038 1038 | obsolete: rewritten using amend as 4:019fadeab383
1039 1039 | summary: A2
1040 1040 |
1041 1041 | @ changeset: 2:fdf9bde5129a
1042 1042 |/ parent: 0:ea207398892e
1043 1043 | user: test
1044 1044 | date: Thu Jan 01 00:00:00 1970 +0000
1045 1045 | instability: content-divergent
1046 1046 | summary: A1
1047 1047 |
1048 1048 | x changeset: 1:471f378eab4c
1049 1049 |/ user: test
1050 1050 | date: Thu Jan 01 00:00:00 1970 +0000
1051 1051 | obsolete: rewritten using amend as 2:fdf9bde5129a
1052 1052 | obsolete: rewritten using amend as 3:65b757b745b9
1053 1053 | summary: A0
1054 1054 |
1055 1055 o changeset: 0:ea207398892e
1056 1056 user: test
1057 1057 date: Thu Jan 01 00:00:00 1970 +0000
1058 1058 summary: ROOT
1059 1059
1060 1060
1061 1061 Test templates with amended + folded commit
1062 1062 ===========================================
1063 1063
1064 1064 Test setup
1065 1065 ----------
1066 1066
1067 1067 $ hg init $TESTTMP/templates-local-amend-fold
1068 1068 $ cd $TESTTMP/templates-local-amend-fold
1069 1069 $ mkcommit ROOT
1070 1070 $ mkcommit A0
1071 1071 $ mkcommit B0
1072 1072 $ hg commit --amend -m "B1"
1073 1073 $ hg log --hidden -G
1074 1074 @ changeset: 3:b7ea6d14e664
1075 1075 | tag: tip
1076 1076 | parent: 1:471f378eab4c
1077 1077 | user: test
1078 1078 | date: Thu Jan 01 00:00:00 1970 +0000
1079 1079 | summary: B1
1080 1080 |
1081 1081 | x changeset: 2:0dec01379d3b
1082 1082 |/ user: test
1083 1083 | date: Thu Jan 01 00:00:00 1970 +0000
1084 1084 | obsolete: rewritten using amend as 3:b7ea6d14e664
1085 1085 | summary: B0
1086 1086 |
1087 1087 o changeset: 1:471f378eab4c
1088 1088 | user: test
1089 1089 | date: Thu Jan 01 00:00:00 1970 +0000
1090 1090 | summary: A0
1091 1091 |
1092 1092 o changeset: 0:ea207398892e
1093 1093 user: test
1094 1094 date: Thu Jan 01 00:00:00 1970 +0000
1095 1095 summary: ROOT
1096 1096
1097 1097 # Simulate a fold
1098 1098 $ hg up -r "desc(ROOT)"
1099 1099 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1100 1100 $ echo "A0" > A0
1101 1101 $ echo "B0" > B0
1102 1102 $ hg commit -A -m "C0"
1103 1103 adding A0
1104 1104 adding B0
1105 1105 created new head
1106 1106 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(C0)"`
1107 1107 obsoleted 1 changesets
1108 1108 $ hg debugobsolete `getid "desc(B1)"` `getid "desc(C0)"`
1109 1109 obsoleted 1 changesets
1110 1110
1111 1111 $ hg log --hidden -G
1112 1112 @ changeset: 4:eb5a0daa2192
1113 1113 | tag: tip
1114 1114 | parent: 0:ea207398892e
1115 1115 | user: test
1116 1116 | date: Thu Jan 01 00:00:00 1970 +0000
1117 1117 | summary: C0
1118 1118 |
1119 1119 | x changeset: 3:b7ea6d14e664
1120 1120 | | parent: 1:471f378eab4c
1121 1121 | | user: test
1122 1122 | | date: Thu Jan 01 00:00:00 1970 +0000
1123 1123 | | obsolete: rewritten as 4:eb5a0daa2192
1124 1124 | | summary: B1
1125 1125 | |
1126 1126 | | x changeset: 2:0dec01379d3b
1127 1127 | |/ user: test
1128 1128 | | date: Thu Jan 01 00:00:00 1970 +0000
1129 1129 | | obsolete: rewritten using amend as 3:b7ea6d14e664
1130 1130 | | summary: B0
1131 1131 | |
1132 1132 | x changeset: 1:471f378eab4c
1133 1133 |/ user: test
1134 1134 | date: Thu Jan 01 00:00:00 1970 +0000
1135 1135 | obsolete: rewritten as 4:eb5a0daa2192
1136 1136 | summary: A0
1137 1137 |
1138 1138 o changeset: 0:ea207398892e
1139 1139 user: test
1140 1140 date: Thu Jan 01 00:00:00 1970 +0000
1141 1141 summary: ROOT
1142 1142
1143 1143 Check templates
1144 1144 ---------------
1145 1145
1146 1146 $ hg up 'desc(A0)' --hidden
1147 1147 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1148 1148
1149 1149 Predecessors template should show current revision as it is the working copy
1150 1150 $ hg tlog
1151 1151 o eb5a0daa2192
1152 1152 | Predecessors: 1:471f378eab4c
1153 1153 | semi-colon: 1:471f378eab4c
1154 1154 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1155 1155 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1156 1156 | @ 471f378eab4c
1157 1157 |/ Successors: 4:eb5a0daa2192
1158 1158 | multi-line: 4:eb5a0daa2192
1159 1159 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1160 1160 o ea207398892e
1161 1161
1162 1162
1163 1163 $ hg fatelog
1164 1164 o eb5a0daa2192
1165 1165 |
1166 1166 | @ 471f378eab4c
1167 1167 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1168 1168 o ea207398892e
1169 1169
1170 1170 $ hg up 'desc(B0)' --hidden
1171 1171 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1172 1172
1173 1173 Predecessors template should both predecessors as they are visible
1174 1174 $ hg tlog
1175 1175 o eb5a0daa2192
1176 1176 | Predecessors: 2:0dec01379d3b 1:471f378eab4c
1177 1177 | semi-colon: 2:0dec01379d3b; 1:471f378eab4c
1178 1178 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", "471f378eab4c5e25f6c77f785b27c936efb22874"]
1179 1179 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5 1:471f378eab4c5e25f6c77f785b27c936efb22874
1180 1180 | @ 0dec01379d3b
1181 1181 | | Successors: 4:eb5a0daa2192
1182 1182 | | multi-line: 4:eb5a0daa2192
1183 1183 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1184 1184 | x 471f378eab4c
1185 1185 |/ Successors: 4:eb5a0daa2192
1186 1186 | multi-line: 4:eb5a0daa2192
1187 1187 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1188 1188 o ea207398892e
1189 1189
1190 1190
1191 1191 $ hg fatelog
1192 1192 o eb5a0daa2192
1193 1193 |
1194 1194 | @ 0dec01379d3b
1195 1195 | | Obsfate: rewritten using amend as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1196 1196 | x 471f378eab4c
1197 1197 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1198 1198 o ea207398892e
1199 1199
1200 1200 $ hg up 'desc(B1)' --hidden
1201 1201 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1202 1202
1203 1203 Predecessors template should both predecessors as they are visible
1204 1204 $ hg tlog
1205 1205 o eb5a0daa2192
1206 1206 | Predecessors: 1:471f378eab4c 3:b7ea6d14e664
1207 1207 | semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
1208 1208 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874", "b7ea6d14e664bdc8922221f7992631b50da3fb07"]
1209 1209 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874 3:b7ea6d14e664bdc8922221f7992631b50da3fb07
1210 1210 | @ b7ea6d14e664
1211 1211 | | Successors: 4:eb5a0daa2192
1212 1212 | | multi-line: 4:eb5a0daa2192
1213 1213 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1214 1214 | x 471f378eab4c
1215 1215 |/ Successors: 4:eb5a0daa2192
1216 1216 | multi-line: 4:eb5a0daa2192
1217 1217 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1218 1218 o ea207398892e
1219 1219
1220 1220
1221 1221 $ hg fatelog
1222 1222 o eb5a0daa2192
1223 1223 |
1224 1224 | @ b7ea6d14e664
1225 1225 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1226 1226 | x 471f378eab4c
1227 1227 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1228 1228 o ea207398892e
1229 1229
1230 1230 $ hg up 'desc(C0)'
1231 1231 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1232 1232
1233 1233 Predecessors template should show no predecessors as they are both non visible
1234 1234 $ hg tlog
1235 1235 @ eb5a0daa2192
1236 1236 |
1237 1237 o ea207398892e
1238 1238
1239 1239
1240 1240 $ hg fatelog
1241 1241 @ eb5a0daa2192
1242 1242 |
1243 1243 o ea207398892e
1244 1244
1245 1245 Predecessors template should show all predecessors as we force their display
1246 1246 with --hidden
1247 1247 $ hg tlog --hidden
1248 1248 @ eb5a0daa2192
1249 1249 | Predecessors: 1:471f378eab4c 3:b7ea6d14e664
1250 1250 | semi-colon: 1:471f378eab4c; 3:b7ea6d14e664
1251 1251 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874", "b7ea6d14e664bdc8922221f7992631b50da3fb07"]
1252 1252 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874 3:b7ea6d14e664bdc8922221f7992631b50da3fb07
1253 1253 | x b7ea6d14e664
1254 1254 | | Predecessors: 2:0dec01379d3b
1255 1255 | | semi-colon: 2:0dec01379d3b
1256 1256 | | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1257 1257 | | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1258 1258 | | Successors: 4:eb5a0daa2192
1259 1259 | | multi-line: 4:eb5a0daa2192
1260 1260 | | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1261 1261 | | x 0dec01379d3b
1262 1262 | |/ Successors: 3:b7ea6d14e664
1263 1263 | | multi-line: 3:b7ea6d14e664
1264 1264 | | json: [["b7ea6d14e664bdc8922221f7992631b50da3fb07"]]
1265 1265 | x 471f378eab4c
1266 1266 |/ Successors: 4:eb5a0daa2192
1267 1267 | multi-line: 4:eb5a0daa2192
1268 1268 | json: [["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]]
1269 1269 o ea207398892e
1270 1270
1271 1271
1272 1272 $ hg fatelog --hidden
1273 1273 @ eb5a0daa2192
1274 1274 |
1275 1275 | x b7ea6d14e664
1276 1276 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1277 1277 | | x 0dec01379d3b
1278 1278 | |/ Obsfate: rewritten using amend as 3:b7ea6d14e664 by test (at 1970-01-01 00:00 +0000);
1279 1279 | x 471f378eab4c
1280 1280 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
1281 1281 o ea207398892e
1282 1282
1283 1283
1284 1284 $ hg fatelogjson --hidden
1285 1285 @ eb5a0daa2192
1286 1286 |
1287 1287 | x b7ea6d14e664
1288 1288 | | Obsfate: [{"markers": [["b7ea6d14e664bdc8922221f7992631b50da3fb07", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
1289 1289 | | x 0dec01379d3b
1290 1290 | |/ Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["b7ea6d14e664bdc8922221f7992631b50da3fb07"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["b7ea6d14e664bdc8922221f7992631b50da3fb07"]}]
1291 1291 | x 471f378eab4c
1292 1292 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["eb5a0daa21923bbf8caeb2c42085b9e463861fd0"]}]
1293 1293 o ea207398892e
1294 1294
1295 1295
1296 1296 Check other fatelog implementations
1297 1297 -----------------------------------
1298 1298
1299 1299 $ hg fatelogkw --hidden -q
1300 1300 @ eb5a0daa2192
1301 1301 |
1302 1302 | x b7ea6d14e664
1303 1303 | | Obsfate: rewritten as 4:eb5a0daa2192
1304 1304 | | x 0dec01379d3b
1305 1305 | |/ Obsfate: rewritten using amend as 3:b7ea6d14e664
1306 1306 | x 471f378eab4c
1307 1307 |/ Obsfate: rewritten as 4:eb5a0daa2192
1308 1308 o ea207398892e
1309 1309
1310 1310 $ hg fatelogkw --hidden
1311 1311 @ eb5a0daa2192
1312 1312 |
1313 1313 | x b7ea6d14e664
1314 1314 | | Obsfate: rewritten as 4:eb5a0daa2192
1315 1315 | | x 0dec01379d3b
1316 1316 | |/ Obsfate: rewritten using amend as 3:b7ea6d14e664
1317 1317 | x 471f378eab4c
1318 1318 |/ Obsfate: rewritten as 4:eb5a0daa2192
1319 1319 o ea207398892e
1320 1320
1321 1321 $ hg fatelogkw --hidden -v
1322 1322 @ eb5a0daa2192
1323 1323 |
1324 1324 | x b7ea6d14e664
1325 1325 | | Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000)
1326 1326 | | x 0dec01379d3b
1327 1327 | |/ Obsfate: rewritten using amend as 3:b7ea6d14e664 by test (at 1970-01-01 00:00 +0000)
1328 1328 | x 471f378eab4c
1329 1329 |/ Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000)
1330 1330 o ea207398892e
1331 1331
1332 1332 $ hg log -G -T "default" --hidden
1333 1333 @ changeset: 4:eb5a0daa2192
1334 1334 | tag: tip
1335 1335 | parent: 0:ea207398892e
1336 1336 | user: test
1337 1337 | date: Thu Jan 01 00:00:00 1970 +0000
1338 1338 | summary: C0
1339 1339 |
1340 1340 | x changeset: 3:b7ea6d14e664
1341 1341 | | parent: 1:471f378eab4c
1342 1342 | | user: test
1343 1343 | | date: Thu Jan 01 00:00:00 1970 +0000
1344 1344 | | obsolete: rewritten as 4:eb5a0daa2192
1345 1345 | | summary: B1
1346 1346 | |
1347 1347 | | x changeset: 2:0dec01379d3b
1348 1348 | |/ user: test
1349 1349 | | date: Thu Jan 01 00:00:00 1970 +0000
1350 1350 | | obsolete: rewritten using amend as 3:b7ea6d14e664
1351 1351 | | summary: B0
1352 1352 | |
1353 1353 | x changeset: 1:471f378eab4c
1354 1354 |/ user: test
1355 1355 | date: Thu Jan 01 00:00:00 1970 +0000
1356 1356 | obsolete: rewritten as 4:eb5a0daa2192
1357 1357 | summary: A0
1358 1358 |
1359 1359 o changeset: 0:ea207398892e
1360 1360 user: test
1361 1361 date: Thu Jan 01 00:00:00 1970 +0000
1362 1362 summary: ROOT
1363 1363
1364 1364
1365 1365 Test template with pushed and pulled obs markers
1366 1366 ================================================
1367 1367
1368 1368 Test setup
1369 1369 ----------
1370 1370
1371 1371 $ hg init $TESTTMP/templates-local-remote-markers-1
1372 1372 $ cd $TESTTMP/templates-local-remote-markers-1
1373 1373 $ mkcommit ROOT
1374 1374 $ mkcommit A0
1375 1375 $ hg clone $TESTTMP/templates-local-remote-markers-1 $TESTTMP/templates-local-remote-markers-2
1376 1376 updating to branch default
1377 1377 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
1378 1378 $ cd $TESTTMP/templates-local-remote-markers-2
1379 1379 $ hg log --hidden -G
1380 1380 @ changeset: 1:471f378eab4c
1381 1381 | tag: tip
1382 1382 | user: test
1383 1383 | date: Thu Jan 01 00:00:00 1970 +0000
1384 1384 | summary: A0
1385 1385 |
1386 1386 o changeset: 0:ea207398892e
1387 1387 user: test
1388 1388 date: Thu Jan 01 00:00:00 1970 +0000
1389 1389 summary: ROOT
1390 1390
1391 1391 $ cd $TESTTMP/templates-local-remote-markers-1
1392 1392 $ hg commit --amend -m "A1"
1393 1393 $ hg commit --amend -m "A2"
1394 1394 $ hg log --hidden -G
1395 1395 @ changeset: 3:7a230b46bf61
1396 1396 | tag: tip
1397 1397 | parent: 0:ea207398892e
1398 1398 | user: test
1399 1399 | date: Thu Jan 01 00:00:00 1970 +0000
1400 1400 | summary: A2
1401 1401 |
1402 1402 | x changeset: 2:fdf9bde5129a
1403 1403 |/ parent: 0:ea207398892e
1404 1404 | user: test
1405 1405 | date: Thu Jan 01 00:00:00 1970 +0000
1406 1406 | obsolete: rewritten using amend as 3:7a230b46bf61
1407 1407 | summary: A1
1408 1408 |
1409 1409 | x changeset: 1:471f378eab4c
1410 1410 |/ user: test
1411 1411 | date: Thu Jan 01 00:00:00 1970 +0000
1412 1412 | obsolete: rewritten using amend as 2:fdf9bde5129a
1413 1413 | summary: A0
1414 1414 |
1415 1415 o changeset: 0:ea207398892e
1416 1416 user: test
1417 1417 date: Thu Jan 01 00:00:00 1970 +0000
1418 1418 summary: ROOT
1419 1419
1420 1420 $ cd $TESTTMP/templates-local-remote-markers-2
1421 1421 $ hg pull
1422 1422 pulling from $TESTTMP/templates-local-remote-markers-1 (glob)
1423 1423 searching for changes
1424 1424 adding changesets
1425 1425 adding manifests
1426 1426 adding file changes
1427 1427 added 1 changesets with 0 changes to 1 files (+1 heads)
1428 1428 2 new obsolescence markers
1429 1429 obsoleted 1 changesets
1430 1430 new changesets 7a230b46bf61
1431 1431 (run 'hg heads' to see heads, 'hg merge' to merge)
1432 1432 $ hg log --hidden -G
1433 1433 o changeset: 2:7a230b46bf61
1434 1434 | tag: tip
1435 1435 | parent: 0:ea207398892e
1436 1436 | user: test
1437 1437 | date: Thu Jan 01 00:00:00 1970 +0000
1438 1438 | summary: A2
1439 1439 |
1440 1440 | @ changeset: 1:471f378eab4c
1441 1441 |/ user: test
1442 1442 | date: Thu Jan 01 00:00:00 1970 +0000
1443 1443 | obsolete: rewritten using amend as 2:7a230b46bf61
1444 1444 | summary: A0
1445 1445 |
1446 1446 o changeset: 0:ea207398892e
1447 1447 user: test
1448 1448 date: Thu Jan 01 00:00:00 1970 +0000
1449 1449 summary: ROOT
1450 1450
1451 1451
1452 1452 $ hg debugobsolete
1453 1453 471f378eab4c5e25f6c77f785b27c936efb22874 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
1454 1454 fdf9bde5129a28d4548fadd3f62b265cdd3b7a2e 7a230b46bf61e50b30308c6cfd7bd1269ef54702 0 (Thu Jan 01 00:00:00 1970 +0000) {'ef1': '1', 'operation': 'amend', 'user': 'test'}
1455 1455
1456 1456 Check templates
1457 1457 ---------------
1458 1458
1459 1459 Predecessors template should show current revision as it is the working copy
1460 1460 $ hg tlog
1461 1461 o 7a230b46bf61
1462 1462 | Predecessors: 1:471f378eab4c
1463 1463 | semi-colon: 1:471f378eab4c
1464 1464 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1465 1465 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1466 1466 | @ 471f378eab4c
1467 1467 |/ Successors: 2:7a230b46bf61
1468 1468 | multi-line: 2:7a230b46bf61
1469 1469 | json: [["7a230b46bf61e50b30308c6cfd7bd1269ef54702"]]
1470 1470 o ea207398892e
1471 1471
1472 1472
1473 1473 $ hg fatelog
1474 1474 o 7a230b46bf61
1475 1475 |
1476 1476 | @ 471f378eab4c
1477 1477 |/ Obsfate: rewritten using amend as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000);
1478 1478 o ea207398892e
1479 1479
1480 1480 $ hg up 'desc(A2)'
1481 1481 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1482 1482
1483 1483 Predecessors template should show no predecessors as they are non visible
1484 1484 $ hg tlog
1485 1485 @ 7a230b46bf61
1486 1486 |
1487 1487 o ea207398892e
1488 1488
1489 1489
1490 1490 $ hg fatelog
1491 1491 @ 7a230b46bf61
1492 1492 |
1493 1493 o ea207398892e
1494 1494
1495 1495 Predecessors template should show all predecessors as we force their display
1496 1496 with --hidden
1497 1497 $ hg tlog --hidden
1498 1498 @ 7a230b46bf61
1499 1499 | Predecessors: 1:471f378eab4c
1500 1500 | semi-colon: 1:471f378eab4c
1501 1501 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1502 1502 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1503 1503 | x 471f378eab4c
1504 1504 |/ Successors: 2:7a230b46bf61
1505 1505 | multi-line: 2:7a230b46bf61
1506 1506 | json: [["7a230b46bf61e50b30308c6cfd7bd1269ef54702"]]
1507 1507 o ea207398892e
1508 1508
1509 1509
1510 1510 $ hg fatelog --hidden
1511 1511 @ 7a230b46bf61
1512 1512 |
1513 1513 | x 471f378eab4c
1514 1514 |/ Obsfate: rewritten using amend as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000);
1515 1515 o ea207398892e
1516 1516
1517 1517
1518 1518 Check other fatelog implementations
1519 1519 -----------------------------------
1520 1520
1521 1521 $ hg fatelogkw --hidden -q
1522 1522 @ 7a230b46bf61
1523 1523 |
1524 1524 | x 471f378eab4c
1525 1525 |/ Obsfate: rewritten using amend as 2:7a230b46bf61
1526 1526 o ea207398892e
1527 1527
1528 1528 $ hg fatelogkw --hidden
1529 1529 @ 7a230b46bf61
1530 1530 |
1531 1531 | x 471f378eab4c
1532 1532 |/ Obsfate: rewritten using amend as 2:7a230b46bf61
1533 1533 o ea207398892e
1534 1534
1535 1535 $ hg fatelogkw --hidden -v
1536 1536 @ 7a230b46bf61
1537 1537 |
1538 1538 | x 471f378eab4c
1539 1539 |/ Obsfate: rewritten using amend as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000)
1540 1540 o ea207398892e
1541 1541
1542 1542 $ hg log -G -T "default" --hidden
1543 1543 @ changeset: 2:7a230b46bf61
1544 1544 | tag: tip
1545 1545 | parent: 0:ea207398892e
1546 1546 | user: test
1547 1547 | date: Thu Jan 01 00:00:00 1970 +0000
1548 1548 | summary: A2
1549 1549 |
1550 1550 | x changeset: 1:471f378eab4c
1551 1551 |/ user: test
1552 1552 | date: Thu Jan 01 00:00:00 1970 +0000
1553 1553 | obsolete: rewritten using amend as 2:7a230b46bf61
1554 1554 | summary: A0
1555 1555 |
1556 1556 o changeset: 0:ea207398892e
1557 1557 user: test
1558 1558 date: Thu Jan 01 00:00:00 1970 +0000
1559 1559 summary: ROOT
1560 1560
1561 1561
1562 1562 Test template with obsmarkers cycle
1563 1563 ===================================
1564 1564
1565 1565 Test setup
1566 1566 ----------
1567 1567
1568 1568 $ hg init $TESTTMP/templates-local-cycle
1569 1569 $ cd $TESTTMP/templates-local-cycle
1570 1570 $ mkcommit ROOT
1571 1571 $ mkcommit A0
1572 1572 $ mkcommit B0
1573 1573 $ hg up -r 0
1574 1574 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
1575 1575 $ mkcommit C0
1576 1576 created new head
1577 1577
1578 1578 Create the cycle
1579 1579
1580 1580 $ hg debugobsolete `getid "desc(A0)"` `getid "desc(B0)"`
1581 1581 obsoleted 1 changesets
1582 1582 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(C0)"`
1583 1583 obsoleted 1 changesets
1584 1584 $ hg debugobsolete `getid "desc(B0)"` `getid "desc(A0)"`
1585 1585
1586 1586 Check templates
1587 1587 ---------------
1588 1588
1589 1589 $ hg tlog
1590 1590 @ f897c6137566
1591 1591 |
1592 1592 o ea207398892e
1593 1593
1594 1594
1595 1595 $ hg fatelog
1596 1596 @ f897c6137566
1597 1597 |
1598 1598 o ea207398892e
1599 1599
1600 1600
1601 1601 $ hg up -r "desc(B0)" --hidden
1602 1602 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
1603 1603 $ hg tlog
1604 1604 o f897c6137566
1605 1605 | Predecessors: 2:0dec01379d3b
1606 1606 | semi-colon: 2:0dec01379d3b
1607 1607 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1608 1608 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1609 1609 | @ 0dec01379d3b
1610 1610 | | Predecessors: 1:471f378eab4c
1611 1611 | | semi-colon: 1:471f378eab4c
1612 1612 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1613 1613 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1614 1614 | | Successors: 3:f897c6137566; 1:471f378eab4c
1615 1615 | | multi-line: 3:f897c6137566
1616 1616 | | multi-line: 1:471f378eab4c
1617 1617 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1618 1618 | x 471f378eab4c
1619 1619 |/ Predecessors: 2:0dec01379d3b
1620 1620 | semi-colon: 2:0dec01379d3b
1621 1621 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1622 1622 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1623 1623 | Successors: 2:0dec01379d3b
1624 1624 | multi-line: 2:0dec01379d3b
1625 1625 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1626 1626 o ea207398892e
1627 1627
1628 1628
1629 1629 $ hg fatelog
1630 1630 o f897c6137566
1631 1631 |
1632 1632 | @ 0dec01379d3b
1633 1633 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000); rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000);
1634 1634 | x 471f378eab4c
1635 1635 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000);
1636 1636 o ea207398892e
1637 1637
1638 1638
1639 1639 $ hg up -r "desc(A0)" --hidden
1640 1640 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1641 1641 $ hg tlog
1642 1642 o f897c6137566
1643 1643 | Predecessors: 1:471f378eab4c
1644 1644 | semi-colon: 1:471f378eab4c
1645 1645 | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1646 1646 | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1647 1647 | @ 471f378eab4c
1648 1648 |/
1649 1649 o ea207398892e
1650 1650
1651 1651
1652 1652 $ hg fatelog
1653 1653 o f897c6137566
1654 1654 |
1655 1655 | @ 471f378eab4c
1656 1656 |/ Obsfate: pruned;
1657 1657 o ea207398892e
1658 1658
1659 1659
1660 1660 $ hg up -r "desc(ROOT)" --hidden
1661 1661 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1662 1662 $ hg tlog
1663 1663 o f897c6137566
1664 1664 |
1665 1665 @ ea207398892e
1666 1666
1667 1667
1668 1668 $ hg fatelog
1669 1669 o f897c6137566
1670 1670 |
1671 1671 @ ea207398892e
1672 1672
1673 1673
1674 1674 $ hg tlog --hidden
1675 1675 o f897c6137566
1676 1676 | Predecessors: 2:0dec01379d3b
1677 1677 | semi-colon: 2:0dec01379d3b
1678 1678 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1679 1679 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1680 1680 | x 0dec01379d3b
1681 1681 | | Predecessors: 1:471f378eab4c
1682 1682 | | semi-colon: 1:471f378eab4c
1683 1683 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
1684 1684 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
1685 1685 | | Successors: 3:f897c6137566; 1:471f378eab4c
1686 1686 | | multi-line: 3:f897c6137566
1687 1687 | | multi-line: 1:471f378eab4c
1688 1688 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
1689 1689 | x 471f378eab4c
1690 1690 |/ Predecessors: 2:0dec01379d3b
1691 1691 | semi-colon: 2:0dec01379d3b
1692 1692 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1693 1693 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
1694 1694 | Successors: 2:0dec01379d3b
1695 1695 | multi-line: 2:0dec01379d3b
1696 1696 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
1697 1697 @ ea207398892e
1698 1698
1699 1699
1700 1700 Check other fatelog implementations
1701 1701 -----------------------------------
1702 1702
1703 1703 $ hg fatelogkw --hidden -q
1704 1704 o f897c6137566
1705 1705 |
1706 1706 | x 0dec01379d3b
1707 1707 | | Obsfate: rewritten as 3:f897c6137566
1708 1708 | | Obsfate: rewritten as 1:471f378eab4c
1709 1709 | x 471f378eab4c
1710 1710 |/ Obsfate: rewritten as 2:0dec01379d3b
1711 1711 @ ea207398892e
1712 1712
1713 1713 $ hg fatelogkw --hidden
1714 1714 o f897c6137566
1715 1715 |
1716 1716 | x 0dec01379d3b
1717 1717 | | Obsfate: rewritten as 3:f897c6137566
1718 1718 | | Obsfate: rewritten as 1:471f378eab4c
1719 1719 | x 471f378eab4c
1720 1720 |/ Obsfate: rewritten as 2:0dec01379d3b
1721 1721 @ ea207398892e
1722 1722
1723 1723 $ hg fatelogkw --hidden -v
1724 1724 o f897c6137566
1725 1725 |
1726 1726 | x 0dec01379d3b
1727 1727 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000)
1728 1728 | | Obsfate: rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000)
1729 1729 | x 471f378eab4c
1730 1730 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000)
1731 1731 @ ea207398892e
1732 1732
1733 1733 $ hg log -G -T "default" --hidden
1734 1734 o changeset: 3:f897c6137566
1735 1735 | tag: tip
1736 1736 | parent: 0:ea207398892e
1737 1737 | user: test
1738 1738 | date: Thu Jan 01 00:00:00 1970 +0000
1739 1739 | summary: C0
1740 1740 |
1741 1741 | x changeset: 2:0dec01379d3b
1742 1742 | | user: test
1743 1743 | | date: Thu Jan 01 00:00:00 1970 +0000
1744 1744 | | obsolete: rewritten as 3:f897c6137566
1745 1745 | | obsolete: rewritten as 1:471f378eab4c
1746 1746 | | summary: B0
1747 1747 | |
1748 1748 | x changeset: 1:471f378eab4c
1749 1749 |/ user: test
1750 1750 | date: Thu Jan 01 00:00:00 1970 +0000
1751 1751 | obsolete: rewritten as 2:0dec01379d3b
1752 1752 | summary: A0
1753 1753 |
1754 1754 @ changeset: 0:ea207398892e
1755 1755 user: test
1756 1756 date: Thu Jan 01 00:00:00 1970 +0000
1757 1757 summary: ROOT
1758 1758
1759 1759
1760 1760 Test template with split + divergence with cycles
1761 1761 =================================================
1762 1762
1763 1763 $ hg log -G
1764 1764 o changeset: 3:f897c6137566
1765 1765 | tag: tip
1766 1766 | parent: 0:ea207398892e
1767 1767 | user: test
1768 1768 | date: Thu Jan 01 00:00:00 1970 +0000
1769 1769 | summary: C0
1770 1770 |
1771 1771 @ changeset: 0:ea207398892e
1772 1772 user: test
1773 1773 date: Thu Jan 01 00:00:00 1970 +0000
1774 1774 summary: ROOT
1775 1775
1776 1776 $ hg up
1777 1777 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1778 1778
1779 1779 Create a commit with three files
1780 1780 $ touch A B C
1781 1781 $ hg commit -A -m "Add A,B,C" A B C
1782 1782
1783 1783 Split it
1784 1784 $ hg up 3
1785 1785 0 files updated, 0 files merged, 3 files removed, 0 files unresolved
1786 1786 $ touch A
1787 1787 $ hg commit -A -m "Add A,B,C" A
1788 1788 created new head
1789 1789
1790 1790 $ touch B
1791 1791 $ hg commit -A -m "Add A,B,C" B
1792 1792
1793 1793 $ touch C
1794 1794 $ hg commit -A -m "Add A,B,C" C
1795 1795
1796 1796 $ hg log -G
1797 1797 @ changeset: 7:ba2ed02b0c9a
1798 1798 | tag: tip
1799 1799 | user: test
1800 1800 | date: Thu Jan 01 00:00:00 1970 +0000
1801 1801 | summary: Add A,B,C
1802 1802 |
1803 1803 o changeset: 6:4a004186e638
1804 1804 | user: test
1805 1805 | date: Thu Jan 01 00:00:00 1970 +0000
1806 1806 | summary: Add A,B,C
1807 1807 |
1808 1808 o changeset: 5:dd800401bd8c
1809 1809 | parent: 3:f897c6137566
1810 1810 | user: test
1811 1811 | date: Thu Jan 01 00:00:00 1970 +0000
1812 1812 | summary: Add A,B,C
1813 1813 |
1814 1814 | o changeset: 4:9bd10a0775e4
1815 1815 |/ user: test
1816 1816 | date: Thu Jan 01 00:00:00 1970 +0000
1817 1817 | summary: Add A,B,C
1818 1818 |
1819 1819 o changeset: 3:f897c6137566
1820 1820 | parent: 0:ea207398892e
1821 1821 | user: test
1822 1822 | date: Thu Jan 01 00:00:00 1970 +0000
1823 1823 | summary: C0
1824 1824 |
1825 1825 o changeset: 0:ea207398892e
1826 1826 user: test
1827 1827 date: Thu Jan 01 00:00:00 1970 +0000
1828 1828 summary: ROOT
1829 1829
1830 1830 $ hg debugobsolete `getid "4"` `getid "5"` `getid "6"` `getid "7"`
1831 1831 obsoleted 1 changesets
1832 1832 $ hg log -G
1833 1833 @ changeset: 7:ba2ed02b0c9a
1834 1834 | tag: tip
1835 1835 | user: test
1836 1836 | date: Thu Jan 01 00:00:00 1970 +0000
1837 1837 | summary: Add A,B,C
1838 1838 |
1839 1839 o changeset: 6:4a004186e638
1840 1840 | user: test
1841 1841 | date: Thu Jan 01 00:00:00 1970 +0000
1842 1842 | summary: Add A,B,C
1843 1843 |
1844 1844 o changeset: 5:dd800401bd8c
1845 1845 | parent: 3:f897c6137566
1846 1846 | user: test
1847 1847 | date: Thu Jan 01 00:00:00 1970 +0000
1848 1848 | summary: Add A,B,C
1849 1849 |
1850 1850 o changeset: 3:f897c6137566
1851 1851 | parent: 0:ea207398892e
1852 1852 | user: test
1853 1853 | date: Thu Jan 01 00:00:00 1970 +0000
1854 1854 | summary: C0
1855 1855 |
1856 1856 o changeset: 0:ea207398892e
1857 1857 user: test
1858 1858 date: Thu Jan 01 00:00:00 1970 +0000
1859 1859 summary: ROOT
1860 1860
1861 1861 Diverge one of the splitted commit
1862 1862
1863 1863 $ hg up 6
1864 1864 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
1865 1865 $ hg commit --amend -m "Add only B"
1866 1866
1867 1867 $ hg up 6 --hidden
1868 1868 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
1869 1869 $ hg commit --amend -m "Add B only"
1870 1870
1871 1871 $ hg log -G
1872 1872 @ changeset: 9:0b997eb7ceee
1873 1873 | tag: tip
1874 1874 | parent: 5:dd800401bd8c
1875 1875 | user: test
1876 1876 | date: Thu Jan 01 00:00:00 1970 +0000
1877 1877 | instability: content-divergent
1878 1878 | summary: Add B only
1879 1879 |
1880 1880 | o changeset: 8:b18bc8331526
1881 1881 |/ parent: 5:dd800401bd8c
1882 1882 | user: test
1883 1883 | date: Thu Jan 01 00:00:00 1970 +0000
1884 1884 | instability: content-divergent
1885 1885 | summary: Add only B
1886 1886 |
1887 1887 | o changeset: 7:ba2ed02b0c9a
1888 1888 | | user: test
1889 1889 | | date: Thu Jan 01 00:00:00 1970 +0000
1890 1890 | | instability: orphan, content-divergent
1891 1891 | | summary: Add A,B,C
1892 1892 | |
1893 1893 | x changeset: 6:4a004186e638
1894 1894 |/ user: test
1895 1895 | date: Thu Jan 01 00:00:00 1970 +0000
1896 1896 | obsolete: rewritten using amend as 8:b18bc8331526
1897 1897 | obsolete: rewritten using amend as 9:0b997eb7ceee
1898 1898 | summary: Add A,B,C
1899 1899 |
1900 1900 o changeset: 5:dd800401bd8c
1901 1901 | parent: 3:f897c6137566
1902 1902 | user: test
1903 1903 | date: Thu Jan 01 00:00:00 1970 +0000
1904 1904 | instability: content-divergent
1905 1905 | summary: Add A,B,C
1906 1906 |
1907 1907 o changeset: 3:f897c6137566
1908 1908 | parent: 0:ea207398892e
1909 1909 | user: test
1910 1910 | date: Thu Jan 01 00:00:00 1970 +0000
1911 1911 | summary: C0
1912 1912 |
1913 1913 o changeset: 0:ea207398892e
1914 1914 user: test
1915 1915 date: Thu Jan 01 00:00:00 1970 +0000
1916 1916 summary: ROOT
1917 1917
1918 1918
1919 1919 Check templates
1920 1920 ---------------
1921 1921
1922 1922 $ hg tlog
1923 1923 @ 0b997eb7ceee
1924 1924 | Predecessors: 6:4a004186e638
1925 1925 | semi-colon: 6:4a004186e638
1926 1926 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1927 1927 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1928 1928 | o b18bc8331526
1929 1929 |/ Predecessors: 6:4a004186e638
1930 1930 | semi-colon: 6:4a004186e638
1931 1931 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1932 1932 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1933 1933 | o ba2ed02b0c9a
1934 1934 | |
1935 1935 | x 4a004186e638
1936 1936 |/ Successors: 8:b18bc8331526; 9:0b997eb7ceee
1937 1937 | multi-line: 8:b18bc8331526
1938 1938 | multi-line: 9:0b997eb7ceee
1939 1939 | json: [["b18bc8331526a22cbb1801022bd1555bf291c48b"], ["0b997eb7ceeee06200a02f8aab185979092d514e"]]
1940 1940 o dd800401bd8c
1941 1941 |
1942 1942 o f897c6137566
1943 1943 |
1944 1944 o ea207398892e
1945 1945
1946 1946 $ hg fatelog
1947 1947 @ 0b997eb7ceee
1948 1948 |
1949 1949 | o b18bc8331526
1950 1950 |/
1951 1951 | o ba2ed02b0c9a
1952 1952 | |
1953 1953 | x 4a004186e638
1954 1954 |/ Obsfate: rewritten using amend as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000); rewritten using amend as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000);
1955 1955 o dd800401bd8c
1956 1956 |
1957 1957 o f897c6137566
1958 1958 |
1959 1959 o ea207398892e
1960 1960
1961 1961 $ hg tlog --hidden
1962 1962 @ 0b997eb7ceee
1963 1963 | Predecessors: 6:4a004186e638
1964 1964 | semi-colon: 6:4a004186e638
1965 1965 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1966 1966 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1967 1967 | o b18bc8331526
1968 1968 |/ Predecessors: 6:4a004186e638
1969 1969 | semi-colon: 6:4a004186e638
1970 1970 | json: ["4a004186e63889f20cb16434fcbd72220bd1eace"]
1971 1971 | map: 6:4a004186e63889f20cb16434fcbd72220bd1eace
1972 1972 | o ba2ed02b0c9a
1973 1973 | | Predecessors: 4:9bd10a0775e4
1974 1974 | | semi-colon: 4:9bd10a0775e4
1975 1975 | | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1976 1976 | | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1977 1977 | x 4a004186e638
1978 1978 |/ Predecessors: 4:9bd10a0775e4
1979 1979 | semi-colon: 4:9bd10a0775e4
1980 1980 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1981 1981 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1982 1982 | Successors: 8:b18bc8331526; 9:0b997eb7ceee
1983 1983 | multi-line: 8:b18bc8331526
1984 1984 | multi-line: 9:0b997eb7ceee
1985 1985 | json: [["b18bc8331526a22cbb1801022bd1555bf291c48b"], ["0b997eb7ceeee06200a02f8aab185979092d514e"]]
1986 1986 o dd800401bd8c
1987 1987 | Predecessors: 4:9bd10a0775e4
1988 1988 | semi-colon: 4:9bd10a0775e4
1989 1989 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
1990 1990 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
1991 1991 | x 9bd10a0775e4
1992 1992 |/ Successors: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
1993 1993 | multi-line: 5:dd800401bd8c 6:4a004186e638 7:ba2ed02b0c9a
1994 1994 | json: [["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"]]
1995 1995 o f897c6137566
1996 1996 | Predecessors: 2:0dec01379d3b
1997 1997 | semi-colon: 2:0dec01379d3b
1998 1998 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
1999 1999 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
2000 2000 | x 0dec01379d3b
2001 2001 | | Predecessors: 1:471f378eab4c
2002 2002 | | semi-colon: 1:471f378eab4c
2003 2003 | | json: ["471f378eab4c5e25f6c77f785b27c936efb22874"]
2004 2004 | | map: 1:471f378eab4c5e25f6c77f785b27c936efb22874
2005 2005 | | Successors: 3:f897c6137566; 1:471f378eab4c
2006 2006 | | multi-line: 3:f897c6137566
2007 2007 | | multi-line: 1:471f378eab4c
2008 2008 | | json: [["f897c6137566320b081514b4c7227ecc3d384b39"], ["471f378eab4c5e25f6c77f785b27c936efb22874"]]
2009 2009 | x 471f378eab4c
2010 2010 |/ Predecessors: 2:0dec01379d3b
2011 2011 | semi-colon: 2:0dec01379d3b
2012 2012 | json: ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]
2013 2013 | map: 2:0dec01379d3be6318c470ead31b1fe7ae7cb53d5
2014 2014 | Successors: 2:0dec01379d3b
2015 2015 | multi-line: 2:0dec01379d3b
2016 2016 | json: [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]]
2017 2017 o ea207398892e
2018 2018
2019 2019 $ hg fatelog --hidden
2020 2020 @ 0b997eb7ceee
2021 2021 |
2022 2022 | o b18bc8331526
2023 2023 |/
2024 2024 | o ba2ed02b0c9a
2025 2025 | |
2026 2026 | x 4a004186e638
2027 2027 |/ Obsfate: rewritten using amend as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000); rewritten using amend as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000);
2028 2028 o dd800401bd8c
2029 2029 |
2030 2030 | x 9bd10a0775e4
2031 2031 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a by test (at 1970-01-01 00:00 +0000);
2032 2032 o f897c6137566
2033 2033 |
2034 2034 | x 0dec01379d3b
2035 2035 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000); rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000);
2036 2036 | x 471f378eab4c
2037 2037 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000);
2038 2038 o ea207398892e
2039 2039
2040 2040 $ hg fatelogjson --hidden
2041 2041 @ 0b997eb7ceee
2042 2042 |
2043 2043 | o b18bc8331526
2044 2044 |/
2045 2045 | o ba2ed02b0c9a
2046 2046 | |
2047 2047 | x 4a004186e638
2048 2048 |/ Obsfate: [{"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["b18bc8331526a22cbb1801022bd1555bf291c48b"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["b18bc8331526a22cbb1801022bd1555bf291c48b"]}, {"markers": [["4a004186e63889f20cb16434fcbd72220bd1eace", ["0b997eb7ceeee06200a02f8aab185979092d514e"], 0, [["ef1", "1"], ["operation", "amend"], ["user", "test"]], [0.0, 0], null]], "successors": ["0b997eb7ceeee06200a02f8aab185979092d514e"]}]
2049 2049 o dd800401bd8c
2050 2050 |
2051 2051 | x 9bd10a0775e4
2052 2052 |/ Obsfate: [{"markers": [["9bd10a0775e478708cada5f176ec6de654359ce7", ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["dd800401bd8c79d815329277739e433e883f784e", "4a004186e63889f20cb16434fcbd72220bd1eace", "ba2ed02b0c9a56b9fdbc4e79c7e57866984d8a1f"]}]
2053 2053 o f897c6137566
2054 2054 |
2055 2055 | x 0dec01379d3b
2056 2056 | | Obsfate: [{"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["f897c6137566320b081514b4c7227ecc3d384b39"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["f897c6137566320b081514b4c7227ecc3d384b39"]}, {"markers": [["0dec01379d3be6318c470ead31b1fe7ae7cb53d5", ["471f378eab4c5e25f6c77f785b27c936efb22874"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["471f378eab4c5e25f6c77f785b27c936efb22874"]}]
2057 2057 | x 471f378eab4c
2058 2058 |/ Obsfate: [{"markers": [["471f378eab4c5e25f6c77f785b27c936efb22874", ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"], 0, [["user", "test"]], [0.0, 0], null]], "successors": ["0dec01379d3be6318c470ead31b1fe7ae7cb53d5"]}]
2059 2059 o ea207398892e
2060 2060
2061 2061 $ hg up --hidden 4
2062 2062 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2063 2063 $ hg rebase -r 7 -d 8 --config extensions.rebase=
2064 2064 rebasing 7:ba2ed02b0c9a "Add A,B,C"
2065 2065 $ hg tlog
2066 2066 o eceed8f98ffc
2067 2067 | Predecessors: 4:9bd10a0775e4
2068 2068 | semi-colon: 4:9bd10a0775e4
2069 2069 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
2070 2070 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
2071 2071 | o 0b997eb7ceee
2072 2072 | | Predecessors: 4:9bd10a0775e4
2073 2073 | | semi-colon: 4:9bd10a0775e4
2074 2074 | | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
2075 2075 | | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
2076 2076 o | b18bc8331526
2077 2077 |/ Predecessors: 4:9bd10a0775e4
2078 2078 | semi-colon: 4:9bd10a0775e4
2079 2079 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
2080 2080 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
2081 2081 o dd800401bd8c
2082 2082 | Predecessors: 4:9bd10a0775e4
2083 2083 | semi-colon: 4:9bd10a0775e4
2084 2084 | json: ["9bd10a0775e478708cada5f176ec6de654359ce7"]
2085 2085 | map: 4:9bd10a0775e478708cada5f176ec6de654359ce7
2086 2086 | @ 9bd10a0775e4
2087 2087 |/ Successors: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc; 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
2088 2088 | multi-line: 5:dd800401bd8c 9:0b997eb7ceee 10:eceed8f98ffc
2089 2089 | multi-line: 5:dd800401bd8c 8:b18bc8331526 10:eceed8f98ffc
2090 2090 | json: [["dd800401bd8c79d815329277739e433e883f784e", "0b997eb7ceeee06200a02f8aab185979092d514e", "eceed8f98ffc4186032e29a6542ab98888ebf68d"], ["dd800401bd8c79d815329277739e433e883f784e", "b18bc8331526a22cbb1801022bd1555bf291c48b", "eceed8f98ffc4186032e29a6542ab98888ebf68d"]]
2091 2091 o f897c6137566
2092 2092 |
2093 2093 o ea207398892e
2094 2094
2095 2095
2096 2096 $ hg fatelog
2097 2097 o eceed8f98ffc
2098 2098 |
2099 2099 | o 0b997eb7ceee
2100 2100 | |
2101 2101 o | b18bc8331526
2102 2102 |/
2103 2103 o dd800401bd8c
2104 2104 |
2105 2105 | @ 9bd10a0775e4
2106 2106 |/ Obsfate: split using amend, rebase as 5:dd800401bd8c, 9:0b997eb7ceee, 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000); split using amend, rebase as 5:dd800401bd8c, 8:b18bc8331526, 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000);
2107 2107 o f897c6137566
2108 2108 |
2109 2109 o ea207398892e
2110 2110
2111 2111 Check other fatelog implementations
2112 2112 -----------------------------------
2113 2113
2114 2114 $ hg fatelogkw --hidden -q
2115 2115 o eceed8f98ffc
2116 2116 |
2117 2117 | o 0b997eb7ceee
2118 2118 | |
2119 2119 o | b18bc8331526
2120 2120 |/
2121 2121 | x ba2ed02b0c9a
2122 2122 | | Obsfate: rewritten using rebase as 10:eceed8f98ffc
2123 2123 | x 4a004186e638
2124 2124 |/ Obsfate: rewritten using amend as 8:b18bc8331526
2125 2125 | Obsfate: rewritten using amend as 9:0b997eb7ceee
2126 2126 o dd800401bd8c
2127 2127 |
2128 2128 | @ 9bd10a0775e4
2129 2129 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a
2130 2130 o f897c6137566
2131 2131 |
2132 2132 | x 0dec01379d3b
2133 2133 | | Obsfate: rewritten as 3:f897c6137566
2134 2134 | | Obsfate: rewritten as 1:471f378eab4c
2135 2135 | x 471f378eab4c
2136 2136 |/ Obsfate: rewritten as 2:0dec01379d3b
2137 2137 o ea207398892e
2138 2138
2139 2139 $ hg fatelogkw --hidden
2140 2140 o eceed8f98ffc
2141 2141 |
2142 2142 | o 0b997eb7ceee
2143 2143 | |
2144 2144 o | b18bc8331526
2145 2145 |/
2146 2146 | x ba2ed02b0c9a
2147 2147 | | Obsfate: rewritten using rebase as 10:eceed8f98ffc
2148 2148 | x 4a004186e638
2149 2149 |/ Obsfate: rewritten using amend as 8:b18bc8331526
2150 2150 | Obsfate: rewritten using amend as 9:0b997eb7ceee
2151 2151 o dd800401bd8c
2152 2152 |
2153 2153 | @ 9bd10a0775e4
2154 2154 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a
2155 2155 o f897c6137566
2156 2156 |
2157 2157 | x 0dec01379d3b
2158 2158 | | Obsfate: rewritten as 3:f897c6137566
2159 2159 | | Obsfate: rewritten as 1:471f378eab4c
2160 2160 | x 471f378eab4c
2161 2161 |/ Obsfate: rewritten as 2:0dec01379d3b
2162 2162 o ea207398892e
2163 2163
2164 2164 $ hg fatelogkw --hidden -v
2165 2165 o eceed8f98ffc
2166 2166 |
2167 2167 | o 0b997eb7ceee
2168 2168 | |
2169 2169 o | b18bc8331526
2170 2170 |/
2171 2171 | x ba2ed02b0c9a
2172 2172 | | Obsfate: rewritten using rebase as 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000)
2173 2173 | x 4a004186e638
2174 2174 |/ Obsfate: rewritten using amend as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000)
2175 2175 | Obsfate: rewritten using amend as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000)
2176 2176 o dd800401bd8c
2177 2177 |
2178 2178 | @ 9bd10a0775e4
2179 2179 |/ Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a by test (at 1970-01-01 00:00 +0000)
2180 2180 o f897c6137566
2181 2181 |
2182 2182 | x 0dec01379d3b
2183 2183 | | Obsfate: rewritten as 3:f897c6137566 by test (at 1970-01-01 00:00 +0000)
2184 2184 | | Obsfate: rewritten as 1:471f378eab4c by test (at 1970-01-01 00:00 +0000)
2185 2185 | x 471f378eab4c
2186 2186 |/ Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000)
2187 2187 o ea207398892e
2188 2188
2189 2189 $ hg log -G -T "default" --hidden
2190 2190 o changeset: 10:eceed8f98ffc
2191 2191 | tag: tip
2192 2192 | parent: 8:b18bc8331526
2193 2193 | user: test
2194 2194 | date: Thu Jan 01 00:00:00 1970 +0000
2195 2195 | instability: content-divergent
2196 2196 | summary: Add A,B,C
2197 2197 |
2198 2198 | o changeset: 9:0b997eb7ceee
2199 2199 | | parent: 5:dd800401bd8c
2200 2200 | | user: test
2201 2201 | | date: Thu Jan 01 00:00:00 1970 +0000
2202 2202 | | instability: content-divergent
2203 2203 | | summary: Add B only
2204 2204 | |
2205 2205 o | changeset: 8:b18bc8331526
2206 2206 |/ parent: 5:dd800401bd8c
2207 2207 | user: test
2208 2208 | date: Thu Jan 01 00:00:00 1970 +0000
2209 2209 | instability: content-divergent
2210 2210 | summary: Add only B
2211 2211 |
2212 2212 | x changeset: 7:ba2ed02b0c9a
2213 2213 | | user: test
2214 2214 | | date: Thu Jan 01 00:00:00 1970 +0000
2215 2215 | | obsolete: rewritten using rebase as 10:eceed8f98ffc
2216 2216 | | summary: Add A,B,C
2217 2217 | |
2218 2218 | x changeset: 6:4a004186e638
2219 2219 |/ user: test
2220 2220 | date: Thu Jan 01 00:00:00 1970 +0000
2221 2221 | obsolete: rewritten using amend as 8:b18bc8331526
2222 2222 | obsolete: rewritten using amend as 9:0b997eb7ceee
2223 2223 | summary: Add A,B,C
2224 2224 |
2225 2225 o changeset: 5:dd800401bd8c
2226 2226 | parent: 3:f897c6137566
2227 2227 | user: test
2228 2228 | date: Thu Jan 01 00:00:00 1970 +0000
2229 2229 | instability: content-divergent
2230 2230 | summary: Add A,B,C
2231 2231 |
2232 2232 | @ changeset: 4:9bd10a0775e4
2233 2233 |/ user: test
2234 2234 | date: Thu Jan 01 00:00:00 1970 +0000
2235 2235 | obsolete: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a
2236 2236 | summary: Add A,B,C
2237 2237 |
2238 2238 o changeset: 3:f897c6137566
2239 2239 | parent: 0:ea207398892e
2240 2240 | user: test
2241 2241 | date: Thu Jan 01 00:00:00 1970 +0000
2242 2242 | summary: C0
2243 2243 |
2244 2244 | x changeset: 2:0dec01379d3b
2245 2245 | | user: test
2246 2246 | | date: Thu Jan 01 00:00:00 1970 +0000
2247 2247 | | obsolete: rewritten as 3:f897c6137566
2248 2248 | | obsolete: rewritten as 1:471f378eab4c
2249 2249 | | summary: B0
2250 2250 | |
2251 2251 | x changeset: 1:471f378eab4c
2252 2252 |/ user: test
2253 2253 | date: Thu Jan 01 00:00:00 1970 +0000
2254 2254 | obsolete: rewritten as 2:0dec01379d3b
2255 2255 | summary: A0
2256 2256 |
2257 2257 o changeset: 0:ea207398892e
2258 2258 user: test
2259 2259 date: Thu Jan 01 00:00:00 1970 +0000
2260 2260 summary: ROOT
2261 2261
2262 2262
2263 2263 Test templates with pruned commits
2264 2264 ==================================
2265 2265
2266 2266 Test setup
2267 2267 ----------
2268 2268
2269 2269 $ hg init $TESTTMP/templates-local-prune
2270 2270 $ cd $TESTTMP/templates-local-prune
2271 2271 $ mkcommit ROOT
2272 2272 $ mkcommit A0
2273 2273 $ hg debugobsolete --record-parent `getid "."`
2274 2274 obsoleted 1 changesets
2275 2275
2276 2276 Check output
2277 2277 ------------
2278 2278
2279 2279 $ hg up "desc(A0)" --hidden
2280 2280 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2281 2281 $ hg tlog
2282 2282 @ 471f378eab4c
2283 2283 |
2284 2284 o ea207398892e
2285 2285
2286 2286 $ hg fatelog
2287 2287 @ 471f378eab4c
2288 2288 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000);
2289 2289 o ea207398892e
2290 2290
2291 2291 Test templates with multiple pruned commits
2292 2292 ===========================================
2293 2293
2294 2294 Test setup
2295 2295 ----------
2296 2296
2297 2297 $ hg init $TESTTMP/multiple-local-prune
2298 2298 $ cd $TESTTMP/multiple-local-prune
2299 2299 $ mkcommit ROOT
2300 2300 $ mkcommit A0
2301 2301 $ hg commit --amend -m "A1"
2302 2302 $ hg debugobsolete --record-parent `getid "."`
2303 2303 obsoleted 1 changesets
2304 2304
2305 2305 $ hg up -r "desc(A0)" --hidden
2306 2306 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2307 2307 $ hg commit --amend -m "A2"
2308 2308 $ hg debugobsolete --record-parent `getid "."`
2309 2309 obsoleted 1 changesets
2310 2310
2311 2311 Check output
2312 2312 ------------
2313 2313
2314 2314 $ hg up "desc(A0)" --hidden
2315 2315 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2316 2316 $ hg tlog
2317 2317 @ 471f378eab4c
2318 2318 |
2319 2319 o ea207398892e
2320 2320
2321 2321 # todo: the obsfate output is not ideal
2322 2322 $ hg fatelog
2323 2323 @ 471f378eab4c
2324 2324 | Obsfate: pruned;
2325 2325 o ea207398892e
2326 2326
2327 2327 $ hg fatelog --hidden
2328 2328 x 65b757b745b9
2329 2329 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000);
2330 2330 | x fdf9bde5129a
2331 2331 |/ Obsfate: pruned by test (at 1970-01-01 00:00 +0000);
2332 2332 | @ 471f378eab4c
2333 2333 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten using amend as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000);
2334 2334 o ea207398892e
2335 2335
2336 2336 Check other fatelog implementations
2337 2337 -----------------------------------
2338 2338
2339 2339 $ hg fatelogkw --hidden -q
2340 2340 x 65b757b745b9
2341 2341 | Obsfate: pruned
2342 2342 | x fdf9bde5129a
2343 2343 |/ Obsfate: pruned
2344 2344 | @ 471f378eab4c
2345 2345 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a
2346 2346 | Obsfate: rewritten using amend as 3:65b757b745b9
2347 2347 o ea207398892e
2348 2348
2349 2349 $ hg fatelogkw --hidden
2350 2350 x 65b757b745b9
2351 2351 | Obsfate: pruned
2352 2352 | x fdf9bde5129a
2353 2353 |/ Obsfate: pruned
2354 2354 | @ 471f378eab4c
2355 2355 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a
2356 2356 | Obsfate: rewritten using amend as 3:65b757b745b9
2357 2357 o ea207398892e
2358 2358
2359 2359 $ hg fatelogkw --hidden -v
2360 2360 x 65b757b745b9
2361 2361 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000)
2362 2362 | x fdf9bde5129a
2363 2363 |/ Obsfate: pruned by test (at 1970-01-01 00:00 +0000)
2364 2364 | @ 471f378eab4c
2365 2365 |/ Obsfate: rewritten using amend as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000)
2366 2366 | Obsfate: rewritten using amend as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000)
2367 2367 o ea207398892e
2368 2368
2369 2369
2370 2370 $ hg log -G -T "default" --hidden
2371 2371 x changeset: 3:65b757b745b9
2372 2372 | tag: tip
2373 2373 | parent: 0:ea207398892e
2374 2374 | user: test
2375 2375 | date: Thu Jan 01 00:00:00 1970 +0000
2376 2376 | obsolete: pruned
2377 2377 | summary: A2
2378 2378 |
2379 2379 | x changeset: 2:fdf9bde5129a
2380 2380 |/ parent: 0:ea207398892e
2381 2381 | user: test
2382 2382 | date: Thu Jan 01 00:00:00 1970 +0000
2383 2383 | obsolete: pruned
2384 2384 | summary: A1
2385 2385 |
2386 2386 | @ changeset: 1:471f378eab4c
2387 2387 |/ user: test
2388 2388 | date: Thu Jan 01 00:00:00 1970 +0000
2389 2389 | obsolete: rewritten using amend as 2:fdf9bde5129a
2390 2390 | obsolete: rewritten using amend as 3:65b757b745b9
2391 2391 | summary: A0
2392 2392 |
2393 2393 o changeset: 0:ea207398892e
2394 2394 user: test
2395 2395 date: Thu Jan 01 00:00:00 1970 +0000
2396 2396 summary: ROOT
2397 2397
2398 2398
2399 2399 Test templates with splitted and pruned commit
2400 2400 ==============================================
2401 2401
2402 2402 $ hg init $TESTTMP/templates-local-split-prune
2403 2403 $ cd $TESTTMP/templates-local-split-prune
2404 2404 $ mkcommit ROOT
2405 2405 $ echo 42 >> a
2406 2406 $ echo 43 >> b
2407 2407 $ hg commit -A -m "A0"
2408 2408 adding a
2409 2409 adding b
2410 2410 $ hg log --hidden -G
2411 2411 @ changeset: 1:471597cad322
2412 2412 | tag: tip
2413 2413 | user: test
2414 2414 | date: Thu Jan 01 00:00:00 1970 +0000
2415 2415 | summary: A0
2416 2416 |
2417 2417 o changeset: 0:ea207398892e
2418 2418 user: test
2419 2419 date: Thu Jan 01 00:00:00 1970 +0000
2420 2420 summary: ROOT
2421 2421
2422 2422 # Simulate split
2423 2423 $ hg up -r "desc(ROOT)"
2424 2424 0 files updated, 0 files merged, 2 files removed, 0 files unresolved
2425 2425 $ echo 42 >> a
2426 2426 $ hg commit -A -m "A1"
2427 2427 adding a
2428 2428 created new head
2429 2429 $ echo 43 >> b
2430 2430 $ hg commit -A -m "A2"
2431 2431 adding b
2432 2432 $ hg debugobsolete `getid "1"` `getid "2"` `getid "3"`
2433 2433 obsoleted 1 changesets
2434 2434
2435 2435 # Simulate prune
2436 2436 $ hg debugobsolete --record-parent `getid "."`
2437 2437 obsoleted 1 changesets
2438 2438
2439 2439 $ hg log --hidden -G
2440 2440 @ changeset: 3:0d0ef4bdf70e
2441 2441 | tag: tip
2442 2442 | user: test
2443 2443 | date: Thu Jan 01 00:00:00 1970 +0000
2444 2444 | obsolete: pruned
2445 2445 | summary: A2
2446 2446 |
2447 2447 o changeset: 2:617adc3a144c
2448 2448 | parent: 0:ea207398892e
2449 2449 | user: test
2450 2450 | date: Thu Jan 01 00:00:00 1970 +0000
2451 2451 | summary: A1
2452 2452 |
2453 2453 | x changeset: 1:471597cad322
2454 2454 |/ user: test
2455 2455 | date: Thu Jan 01 00:00:00 1970 +0000
2456 2456 | obsolete: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2457 2457 | summary: A0
2458 2458 |
2459 2459 o changeset: 0:ea207398892e
2460 2460 user: test
2461 2461 date: Thu Jan 01 00:00:00 1970 +0000
2462 2462 summary: ROOT
2463 2463
2464 2464 Check templates
2465 2465 ---------------
2466 2466
2467 2467 $ hg up 'desc("A0")' --hidden
2468 2468 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2469 2469
2470 2470 # todo: the obsfate output is not ideal
2471 2471 $ hg fatelog
2472 2472 o 617adc3a144c
2473 2473 |
2474 2474 | @ 471597cad322
2475 2475 |/ Obsfate: pruned;
2476 2476 o ea207398892e
2477 2477
2478 2478 $ hg up -r 'desc("A2")' --hidden
2479 2479 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
2480 2480
2481 2481 $ hg fatelog --hidden
2482 2482 @ 0d0ef4bdf70e
2483 2483 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000);
2484 2484 o 617adc3a144c
2485 2485 |
2486 2486 | x 471597cad322
2487 2487 |/ Obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e by test (at 1970-01-01 00:00 +0000);
2488 2488 o ea207398892e
2489 2489
2490 2490
2491 2491 Check other fatelog implementations
2492 2492 -----------------------------------
2493 2493
2494 2494 $ hg fatelogkw --hidden -q
2495 2495 @ 0d0ef4bdf70e
2496 2496 | Obsfate: pruned
2497 2497 o 617adc3a144c
2498 2498 |
2499 2499 | x 471597cad322
2500 2500 |/ Obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2501 2501 o ea207398892e
2502 2502
2503 2503 $ hg fatelogkw --hidden
2504 2504 @ 0d0ef4bdf70e
2505 2505 | Obsfate: pruned
2506 2506 o 617adc3a144c
2507 2507 |
2508 2508 | x 471597cad322
2509 2509 |/ Obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2510 2510 o ea207398892e
2511 2511
2512 2512 $ hg fatelogkw --hidden -v
2513 2513 @ 0d0ef4bdf70e
2514 2514 | Obsfate: pruned by test (at 1970-01-01 00:00 +0000)
2515 2515 o 617adc3a144c
2516 2516 |
2517 2517 | x 471597cad322
2518 2518 |/ Obsfate: split as 2:617adc3a144c, 3:0d0ef4bdf70e by test (at 1970-01-01 00:00 +0000)
2519 2519 o ea207398892e
2520 2520
2521 2521 $ hg log -G -T "default" --hidden
2522 2522 @ changeset: 3:0d0ef4bdf70e
2523 2523 | tag: tip
2524 2524 | user: test
2525 2525 | date: Thu Jan 01 00:00:00 1970 +0000
2526 2526 | obsolete: pruned
2527 2527 | summary: A2
2528 2528 |
2529 2529 o changeset: 2:617adc3a144c
2530 2530 | parent: 0:ea207398892e
2531 2531 | user: test
2532 2532 | date: Thu Jan 01 00:00:00 1970 +0000
2533 2533 | summary: A1
2534 2534 |
2535 2535 | x changeset: 1:471597cad322
2536 2536 |/ user: test
2537 2537 | date: Thu Jan 01 00:00:00 1970 +0000
2538 2538 | obsolete: split as 2:617adc3a144c, 3:0d0ef4bdf70e
2539 2539 | summary: A0
2540 2540 |
2541 2541 o changeset: 0:ea207398892e
2542 2542 user: test
2543 2543 date: Thu Jan 01 00:00:00 1970 +0000
2544 2544 summary: ROOT
2545 2545
General Comments 0
You need to be logged in to leave comments. Login now