changeset 2591:1991935fb603

obsfate: add a new obsfate template The obsfate template display for each obsolete changeset a line summarizing what changed between the changeset and its successors. This dict is computed in obshistory._preparesuccessorset. It uses obshistory.FORMATSSETSFUNCTIONS which is a list of function that individually compute a part of each dict. You can override fields or add new ones by adding your own function in this list. The format of obsfate is computed in templatekw.obsfatedefaulttempl and can be wrapped if necessary, the code is not quite extendable for the moment but can be refactored later.
author Boris Feld <boris.feld@octobus.net>
date Fri, 09 Jun 2017 00:52:54 +0100
parents 0d7dec71646d
children fb33d856d25e
files hgext3rd/evolve/__init__.py hgext3rd/evolve/obshistory.py hgext3rd/evolve/templatekw.py tests/test-evolve-templates.t
diffstat 4 files changed, 303 insertions(+), 94 deletions(-) [+]
line wrap: on
line diff
--- a/hgext3rd/evolve/__init__.py	Fri Jun 09 00:31:00 2017 +0100
+++ b/hgext3rd/evolve/__init__.py	Fri Jun 09 00:52:54 2017 +0100
@@ -125,6 +125,23 @@
     successors. It is useful when your working directory is obsolete to see
     what are its successors. This informations can also be retrieved with the
     obslog command and the --all option.
+  - obsfate, for each obsolete changeset display a line summarizing what
+    changed between the changeset and its successors. Dependending on the
+    verbosity level (-q and -v) it display the changeset successors, the users
+    that created the obsmarkers and the date range of theses changes.
+
+    The template itself is not complex, the data are basically a list of
+    successortset. Each successorset is a dict with these fields:
+
+      - "verb", how did the revision changed, pruned or rewritten for the moment
+      - "users" a sorted list of users that have create obs marker between current
+        changeset and one of its successor
+      - "min_date" the tiniest date of the first obs marker between current
+        changeset and one of its successor
+      - "max_date" the biggest date between current changeset and one of its
+        successor
+      - "successors" a sorted list of locally know successors node ids
+      - "markers" the raw list of changesets.
 """
 
 evolutionhelptext = """
--- a/hgext3rd/evolve/obshistory.py	Fri Jun 09 00:31:00 2017 +0100
+++ b/hgext3rd/evolve/obshistory.py	Fri Jun 09 00:52:54 2017 +0100
@@ -597,16 +597,89 @@
 
     return (fate, successors)
 
-def _humanizedobsfate(fate, successors):
-    """ Returns a humanized string for a changeset fate and its successors
+def _successorsetdates(successorset, markers):
+    """returns the max date and the min date of the markers list
     """
 
-    if fate == 'pruned':
-        return 'pruned'
-    elif fate == 'diverged':
-        msgs = []
-        for successorsset in successors:
-            msgs.append('superseed as %s' % ','.join(successorsset))
-        return ' + '.join(msgs)
-    elif fate in ('superseed', 'superseed_split'):
-        return 'superseed as %s' % ','.join(successors)
+    if not markers:
+        return {}
+
+    dates = [m[4] for m in markers]
+
+    return {
+        'min_date': min(dates),
+        'max_date': max(dates)
+    }
+
+def _successorsetusers(successorset, markers):
+    """ Returns a sorted list of markers users without duplicates
+    """
+    if not markers:
+        return {}
+
+    # Check that user is present in meta
+    markersmeta = [dict(m[3]) for m in markers]
+    users = set(meta.get('user') for meta in markersmeta if meta.get('user'))
+
+    return {'users': sorted(users)}
+
+def _successorsetverb(successorset, markers):
+    """ Return the verb summarizing the successorset
+    """
+    # XXX we need to handle prune markers at some point.
+    if not markers:
+        return {'verb': 'pruned'}
+
+    return {'verb': 'rewritten'}
+
+FORMATSSETSFUNCTIONS = [
+    _successorsetdates,
+    _successorsetusers,
+    _successorsetverb
+]
+
+def _successorsetallmarkers(successorset, pathscache):
+    """compute all successors of a successorset.
+
+    pathscache must contains all successors starting from selected nodes
+    or revision. This way, iterating on each successor, we can take all
+    precursors and have the subgraph of all obsmarkers between roots to
+    successors.
+    """
+
+    markers = set()
+    seen = set()
+
+    for successor in successorset:
+        stack = [successor]
+
+        while stack:
+            element = stack.pop()
+            seen.add(element)
+            for prec, mark in pathscache.get(element, []):
+                if prec not in seen:
+                    # Process element precursors
+                    stack.append(prec)
+
+                if mark not in markers:
+                    markers.add(mark)
+
+    return markers
+
+def _preparesuccessorset(successorset, pathscache):
+    """ For a successor set, get all related markers, compute the set of user,
+    the min date and the max date
+    """
+    markers = _successorsetallmarkers(successorset, pathscache)
+
+    # Format basic data
+    data = {
+        "successors": sorted(successorset),
+        "markers": sorted(markers)
+    }
+
+    # Call an extensible list of functions to override or add new data
+    for function in FORMATSSETSFUNCTIONS:
+        data.update(function(successorset, markers))
+
+    return data
--- a/hgext3rd/evolve/templatekw.py	Fri Jun 09 00:31:00 2017 +0100
+++ b/hgext3rd/evolve/templatekw.py	Fri Jun 09 00:52:54 2017 +0100
@@ -91,7 +91,7 @@
     if not ctx.obsolete():
         return ''
 
-    ssets = closestsuccessors(repo, ctx.node())
+    ssets, _ = closestsuccessors(repo, ctx.node())
 
     data = []
     gen = []
@@ -106,13 +106,66 @@
     return templatekw._hybrid(iter(gen), data, lambda x: {'successorset': x},
                               lambda d: d["successorset"])
 
-@eh.templatekw("obsfate_quiet")
-def showobsfate_quiet(repo, ctx, templ, **args):
+def obsfatedefaulttempl():
+    """ Returns a dict with the default templates for obs fate
+    """
+    # Prepare templates
+    verbtempl = '{verb}'
+    usertempl = '{if(users, " by {join(users, ", ")}")}'
+    succtempl = '{if(successors, " as ")}{successors}' # Bypass if limitation
+    datetempleq = ' (at {min_date|isodate})'
+    datetemplnoteq = ' (between {min_date|isodate} and {max_date|isodate})'
+    datetempl = '{if(max_date, "{ifeq(min_date, max_date, "%s", "%s")}")}' % (datetempleq, datetemplnoteq)
+    newline = '\n'
+
+    # Assemble them
+    return {
+        'obsfate_quiet': verbtempl + succtempl + newline,
+        'obsfate': verbtempl + usertempl + succtempl + newline,
+        'obsfate_verbose': verbtempl + usertempl + succtempl + datetempl + newline
+    }
+
+@eh.templatekw("obsfate")
+def showobsfate(repo, ctx, **args):
     if not ctx.obsolete():
         return ''
 
-    successorssets = closestsuccessors(repo, ctx.node())
-    return obshistory._humanizedobsfate(*obshistory._getobsfateandsuccs(repo, ctx, successorssets))
+    successorssets, pathcache = closestsuccessors(repo, ctx.node())
+
+    # closestsuccessors returns an empty list for pruned revisions, remap it
+    # into a list containing en empty list for future processing
+    if successorssets == []:
+        successorssets = [[]]
+
+    values = []
+    for successorset in successorssets:
+        raw = obshistory._preparesuccessorset(successorset, pathcache)
+
+        # As we can't do something like
+        # "{join(map(nodeshort, successors), ', '}" in template, manually
+        # create a correct textual representation
+        gen = ', '.join(map(node.short, raw['successors']))
+
+        makemap = lambda x: {'successor': x}
+        joinfmt = lambda d: "%s" % d['successor']
+        raw['successors'] = templatekw._hybrid(gen, raw['successors'], makemap,
+                                               joinfmt)
+
+        values.append(raw)
+
+    # Insert default obsfate templates
+    args['templ'].cache.update(obsfatedefaulttempl())
+
+    if repo.ui.quiet:
+        name = "obsfate_quiet"
+    elif repo.ui.verbose:
+        name = "obsfate_verbose"
+    elif repo.ui.debugflag:
+        name = "obsfate_debug"
+    else:
+        name = "obsfate"
+
+    return templatekw.showlist(name, values, args, separator=' + ')
 
 # copy from mercurial.obsolete with a small change to stop at first known changeset.
 
@@ -127,6 +180,9 @@
     # set version of above list for fast loop detection
     # element added to "toproceed" must be added here
     stackedset = set(toproceed)
+
+    pathscache = {}
+
     if cache is None:
         cache = {}
     while toproceed:
@@ -156,6 +212,7 @@
                             # of one of those successors we add it to the
                             # `toproceed` stack and stop all work for this
                             # iteration.
+                            pathscache.setdefault(suc, []).append((current, mark))
                             toproceed.append(suc)
                             stackedset.add(suc)
                             break
@@ -195,4 +252,5 @@
                         seen.append(setversion)
                 final.reverse() # put small successors set first
                 cache[current] = final
-    return cache[initialnode]
+
+    return cache[initialnode], pathscache
--- a/tests/test-evolve-templates.t	Fri Jun 09 00:31:00 2017 +0100
+++ b/tests/test-evolve-templates.t	Fri Jun 09 00:52:54 2017 +0100
@@ -17,9 +17,9 @@
   >     {if(precursors, "\n  semi-colon: {join(precursors, "; ")}")}\
   >     {if(successors, "\n  Successors: {successors}")}\
   >     {if(successors, "\n  semi-colon: {join(successors, "; ")}")}\
-  >     {if(obsfate_quiet, "\n  Fate: {obsfate_quiet}")}\n'
-  > fatelog = log -G -T '{node|short}\n'
-  > fatelogjson = log -G -T '{node|short}\n'
+  >     {if(obsfate, "\n  Fate: {obsfate}")}\n'
+  > fatelog = log -G -T '{node|short}\n{if(obsfate, "  Obsfate: {obsfate}\n")}'
+  > fatelogjson = log -G -T '{node|short} {obsfate|json}\n'
   > EOF
 
 Test templates on amended commit
@@ -88,28 +88,33 @@
   | @  471f378eab4c
   |/     Successors: [d004c8f274b9]
   |      semi-colon: [d004c8f274b9]
-  |      Fate: superseed as d004c8f274b9
+  |      Fate: rewritten by test1, test2 as d004c8f274b9
+  |
   o  ea207398892e
   
   $ hg fatelog -q
   o  d004c8f274b9
   |
   | @  471f378eab4c
-  |/
+  |/     Obsfate: rewritten as d004c8f274b9
+  |
   o  ea207398892e
   
+
   $ hg fatelog
   o  d004c8f274b9
   |
   | @  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test1, test2 as d004c8f274b9
+  |
   o  ea207398892e
   
   $ hg fatelog -v
   o  d004c8f274b9
   |
   | @  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test1, test2 as d004c8f274b9 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000)
+  |
   o  ea207398892e
   
   $ hg up 'desc(A1)' --hidden
@@ -125,7 +130,8 @@
   | @  a468dc9b3633
   |/     Successors: [d004c8f274b9]
   |      semi-colon: [d004c8f274b9]
-  |      Fate: superseed as d004c8f274b9
+  |      Fate: rewritten by test2 as d004c8f274b9
+  |
   o  ea207398892e
   
 Precursors template should show the precursor as we force its display with
@@ -139,20 +145,24 @@
   |      semi-colon: 471f378eab4c
   |      Successors: [d004c8f274b9]
   |      semi-colon: [d004c8f274b9]
-  |      Fate: superseed as d004c8f274b9
+  |      Fate: rewritten by test2 as d004c8f274b9
+  |
   | x  f137d23bb3e1
   | |    Fate: pruned
+  | |
   | x  471f378eab4c
   |/     Successors: [a468dc9b3633]
   |      semi-colon: [a468dc9b3633]
-  |      Fate: superseed as a468dc9b3633
+  |      Fate: rewritten by test1 as a468dc9b3633
+  |
   o  ea207398892e
   
   $ hg fatelog -v
   o  d004c8f274b9
   |
   | @  a468dc9b3633
-  |/
+  |/     Obsfate: rewritten by test2 as d004c8f274b9 (at 2001-04-19 04:25 +0000)
+  |
   o  ea207398892e
   
   $ hg up 'desc(A2)'
@@ -171,13 +181,16 @@
   |      semi-colon: 471f378eab4c
   |      Successors: [d004c8f274b9]
   |      semi-colon: [d004c8f274b9]
-  |      Fate: superseed as d004c8f274b9
+  |      Fate: rewritten by test2 as d004c8f274b9
+  |
   | x  f137d23bb3e1
   | |    Fate: pruned
+  | |
   | x  471f378eab4c
   |/     Successors: [a468dc9b3633]
   |      semi-colon: [a468dc9b3633]
-  |      Fate: superseed as a468dc9b3633
+  |      Fate: rewritten by test1 as a468dc9b3633
+  |
   o  ea207398892e
   
   $ hg fatelog -v
@@ -190,24 +203,27 @@
   @  d004c8f274b9
   |
   | x  a468dc9b3633
-  |/
+  |/     Obsfate: rewritten by test2 as d004c8f274b9 (at 2001-04-19 04:25 +0000)
+  |
   | x  f137d23bb3e1
+  | |    Obsfate: pruned
   | |
   | x  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test1 as a468dc9b3633 (at 2009-02-13 23:31 +0000)
+  |
   o  ea207398892e
   
 
   $ hg fatelogjson --hidden
-  @  d004c8f274b9
+  @  d004c8f274b9 ""
   |
-  | x  a468dc9b3633
+  | x  a468dc9b3633 [{"markers": [["\udca4h\u071b63\udc8b\u0014\udcfd\udcb7\udc82_U\udcce=\udcf4\udce7\u0015\u0017\udcad", ["\udcd0\u0004\udcc8\udcf2t\udcb9\udcecH\nG\udca9\u003c\u0010\udcda\udcc5\udcee\udce6:\udcdbx"], 0, [["ef1", "1"], ["user", "test2"]], [987654321.0, 0], null]], "max_date": [987654321.0, 0], "min_date": [987654321.0, 0], "successors": ["\udcd0\u0004\udcc8\udcf2t\udcb9\udcecH\nG\udca9\u003c\u0010\udcda\udcc5\udcee\udce6:\udcdbx"], "users": ["test2"], "verb": "rewritten"}]
   |/
-  | x  f137d23bb3e1
+  | x  f137d23bb3e1 [{"markers": [], "successors": [], "verb": "pruned"}]
   | |
-  | x  471f378eab4c
+  | x  471f378eab4c [{"markers": [["G\u001f7\udc8e\udcabL^%\udcf6\udcc7\u007fx['\udcc96\udcef\udcb2(t", ["\udca4h\u071b63\udc8b\u0014\udcfd\udcb7\udc82_U\udcce=\udcf4\udce7\u0015\u0017\udcad"], 0, [["ef1", "9"], ["user", "test1"]], [1234567890.0, 0], null]], "max_date": [1234567890.0, 0], "min_date": [1234567890.0, 0], "successors": ["\udca4h\u071b63\udc8b\u0014\udcfd\udcb7\udc82_U\udcce=\udcf4\udce7\u0015\u0017\udcad"], "users": ["test1"], "verb": "rewritten"}]
   |/
-  o  ea207398892e
+  o  ea207398892e ""
   
 
 Test templates with splitted commit
@@ -311,7 +327,8 @@
   | @  471597cad322
   |/     Successors: [337fec4d2edc, f257fde29c7a]
   |      semi-colon: [337fec4d2edc, f257fde29c7a]
-  |      Fate: superseed as 337fec4d2edc,f257fde29c7a
+  |      Fate: rewritten by test as 337fec4d2edc, f257fde29c7a
+  |
   o  ea207398892e
   
   $ hg fatelog
@@ -320,7 +337,8 @@
   o  337fec4d2edc
   |
   | @  471597cad322
-  |/
+  |/     Obsfate: rewritten by test as 337fec4d2edc, f257fde29c7a
+  |
   o  ea207398892e
   
 
@@ -348,7 +366,8 @@
   | x  471597cad322
   |/     Successors: [337fec4d2edc, f257fde29c7a]
   |      semi-colon: [337fec4d2edc, f257fde29c7a]
-  |      Fate: superseed as 337fec4d2edc,f257fde29c7a
+  |      Fate: rewritten by test as 337fec4d2edc, f257fde29c7a
+  |
   o  ea207398892e
   
   $ hg fatelog --hidden
@@ -357,18 +376,19 @@
   o  337fec4d2edc
   |
   | x  471597cad322
-  |/
+  |/     Obsfate: rewritten by test as 337fec4d2edc, f257fde29c7a
+  |
   o  ea207398892e
   
 
   $ hg fatelogjson --hidden
-  @  f257fde29c7a
+  @  f257fde29c7a ""
   |
-  o  337fec4d2edc
+  o  337fec4d2edc ""
   |
-  | x  471597cad322
+  | x  471597cad322 [{"markers": [["G\u0015\udc97\udcca\udcd3\"\udcd1\udcf6Y\udcbb\u0016\udc97Q\udcbe\udc913\udcda\udcd9.\udcf3", ["3\u007f\udcecM.\udcdc\udcf0\udce7\udca4g\udce3_\udc81\udc824\udcbcb\u0000h\udcb5", "\udcf2W\udcfd\udce2\udc9cz\udc84|\udc9b`\u007fn\udc95\udc86V\udcd0\udcdf\u000f\udcb1\\"], 0, [["ef1", "12"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["3\u007f\udcecM.\udcdc\udcf0\udce7\udca4g\udce3_\udc81\udc824\udcbcb\u0000h\udcb5", "\udcf2W\udcfd\udce2\udc9cz\udc84|\udc9b`\u007fn\udc95\udc86V\udcd0\udcdf\u000f\udcb1\\"], "users": ["test"], "verb": "rewritten"}]
   |/
-  o  ea207398892e
+  o  ea207398892e ""
   
 
 Test templates with folded commit
@@ -441,14 +461,16 @@
   | @  471f378eab4c
   |/     Successors: [eb5a0daa2192]
   |      semi-colon: [eb5a0daa2192]
-  |      Fate: superseed as eb5a0daa2192
+  |      Fate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg fatelog
   o  eb5a0daa2192
   |
   | @  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg up 'desc(B0)' --hidden
@@ -465,20 +487,24 @@
   | @  0dec01379d3b
   | |    Successors: [eb5a0daa2192]
   | |    semi-colon: [eb5a0daa2192]
-  | |    Fate: superseed as eb5a0daa2192
+  | |    Fate: rewritten by test as eb5a0daa2192
+  | |
   | x  471f378eab4c
   |/     Successors: [eb5a0daa2192]
   |      semi-colon: [eb5a0daa2192]
-  |      Fate: superseed as eb5a0daa2192
+  |      Fate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg fatelog
   o  eb5a0daa2192
   |
   | @  0dec01379d3b
+  | |    Obsfate: rewritten by test as eb5a0daa2192
   | |
   | x  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
 
@@ -501,31 +527,35 @@
   | x  0dec01379d3b
   | |    Successors: [eb5a0daa2192]
   | |    semi-colon: [eb5a0daa2192]
-  | |    Fate: superseed as eb5a0daa2192
+  | |    Fate: rewritten by test as eb5a0daa2192
+  | |
   | x  471f378eab4c
   |/     Successors: [eb5a0daa2192]
   |      semi-colon: [eb5a0daa2192]
-  |      Fate: superseed as eb5a0daa2192
+  |      Fate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg fatelog --hidden
   @  eb5a0daa2192
   |
   | x  0dec01379d3b
+  | |    Obsfate: rewritten by test as eb5a0daa2192
   | |
   | x  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
 
   $ hg fatelogjson --hidden
-  @  eb5a0daa2192
+  @  eb5a0daa2192 ""
   |
-  | x  0dec01379d3b
+  | x  0dec01379d3b [{"markers": [["\r\udcec\u00017\udc9d;\udce61\udc8cG\u000e\udcad1\udcb1\udcfez\udce7\udccbS\udcd5", ["\udcebZ\r\udcaa!\udc92;\udcbf\udc8c\udcae\udcb2\udcc4 \udc85\udcb9\udce4c\udc86\u001f\udcd0"], 0, [["ef1", "13"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["\udcebZ\r\udcaa!\udc92;\udcbf\udc8c\udcae\udcb2\udcc4 \udc85\udcb9\udce4c\udc86\u001f\udcd0"], "users": ["test"], "verb": "rewritten"}]
   | |
-  | x  471f378eab4c
+  | x  471f378eab4c [{"markers": [["G\u001f7\udc8e\udcabL^%\udcf6\udcc7\u007fx['\udcc96\udcef\udcb2(t", ["\udcebZ\r\udcaa!\udc92;\udcbf\udc8c\udcae\udcb2\udcc4 \udc85\udcb9\udce4c\udc86\u001f\udcd0"], 0, [["ef1", "9"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["\udcebZ\r\udcaa!\udc92;\udcbf\udc8c\udcae\udcb2\udcc4 \udc85\udcb9\udce4c\udc86\u001f\udcd0"], "users": ["test"], "verb": "rewritten"}]
   |/
-  o  ea207398892e
+  o  ea207398892e ""
   
 
 Test templates with divergence
@@ -610,7 +640,9 @@
   | @  471f378eab4c
   |/     Successors: [fdf9bde5129a], [019fadeab383]
   |      semi-colon: [fdf9bde5129a]; [019fadeab383]
-  |      Fate: superseed as fdf9bde5129a + superseed as 019fadeab383
+  |      Fate: rewritten by test as fdf9bde5129a
+  |    rewritten by test as 019fadeab383
+  |
   o  ea207398892e
   
   $ hg fatelog
@@ -619,7 +651,9 @@
   | o  fdf9bde5129a
   |/
   | @  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as fdf9bde5129a
+  |    rewritten by test as 019fadeab383
+  |
   o  ea207398892e
   
 
@@ -652,38 +686,44 @@
   |      semi-colon: 471f378eab4c
   |      Successors: [019fadeab383]
   |      semi-colon: [019fadeab383]
-  |      Fate: superseed as 019fadeab383
+  |      Fate: rewritten by test as 019fadeab383
+  |
   | @  fdf9bde5129a
   |/     Precursors: 471f378eab4c
   |      semi-colon: 471f378eab4c
   | x  471f378eab4c
   |/     Successors: [fdf9bde5129a], [65b757b745b9]
   |      semi-colon: [fdf9bde5129a]; [65b757b745b9]
-  |      Fate: superseed as fdf9bde5129a + superseed as 65b757b745b9
+  |      Fate: rewritten by test as fdf9bde5129a
+  |    rewritten by test as 65b757b745b9
+  |
   o  ea207398892e
   
   $ hg fatelog --hidden
   o  019fadeab383
   |
   | x  65b757b745b9
-  |/
+  |/     Obsfate: rewritten by test as 019fadeab383
+  |
   | @  fdf9bde5129a
   |/
   | x  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as fdf9bde5129a
+  |    rewritten by test as 65b757b745b9
+  |
   o  ea207398892e
   
 
   $ hg fatelogjson --hidden
-  o  019fadeab383
+  o  019fadeab383 ""
   |
-  | x  65b757b745b9
+  | x  65b757b745b9 [{"markers": [["e\udcb7W\udcb7E\udcb95\t\u003c\udc87\udca2\udcbc\u0347u!\udccc\udccf\udcfc\udcbd", ["\u0001\udc9f\udcad\uacc3\udcf6i\udc9f\udca8:\u05fd\udcb4\udcd8.\udcd2\udcc0\udce5\udcab"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["\u0001\udc9f\udcad\uacc3\udcf6i\udc9f\udca8:\u05fd\udcb4\udcd8.\udcd2\udcc0\udce5\udcab"], "users": ["test"], "verb": "rewritten"}]
   |/
-  | @  fdf9bde5129a
+  | @  fdf9bde5129a ""
   |/
-  | x  471f378eab4c
+  | x  471f378eab4c [{"markers": [["G\u001f7\udc8e\udcabL^%\udcf6\udcc7\u007fx['\udcc96\udcef\udcb2(t", ["\udcfd\udcf9\udcbd\udce5\u0012\udc9a(\udcd4T\udc8f\udcad\udcd3\udcf6+&\\\udcdd;z."], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["\udcfd\udcf9\udcbd\udce5\u0012\udc9a(\udcd4T\udc8f\udcad\udcd3\udcf6+&\\\udcdd;z."], "users": ["test"], "verb": "rewritten"}, {"markers": [["G\u001f7\udc8e\udcabL^%\udcf6\udcc7\u007fx['\udcc96\udcef\udcb2(t", ["e\udcb7W\udcb7E\udcb95\t\u003c\udc87\udca2\udcbc\u0347u!\udccc\udccf\udcfc\udcbd"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["e\udcb7W\udcb7E\udcb95\t\u003c\udc87\udca2\udcbc\u0347u!\udccc\udccf\udcfc\udcbd"], "users": ["test"], "verb": "rewritten"}]
   |/
-  o  ea207398892e
+  o  ea207398892e ""
   
 
 Test templates with amended + folded commit
@@ -767,14 +807,16 @@
   | @  471f378eab4c
   |/     Successors: [eb5a0daa2192]
   |      semi-colon: [eb5a0daa2192]
-  |      Fate: superseed as eb5a0daa2192
+  |      Fate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg fatelog
   o  eb5a0daa2192
   |
   | @  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg up 'desc(B0)' --hidden
@@ -788,20 +830,24 @@
   | @  0dec01379d3b
   | |    Successors: [eb5a0daa2192]
   | |    semi-colon: [eb5a0daa2192]
-  | |    Fate: superseed as eb5a0daa2192
+  | |    Fate: rewritten by test as eb5a0daa2192
+  | |
   | x  471f378eab4c
   |/     Successors: [eb5a0daa2192]
   |      semi-colon: [eb5a0daa2192]
-  |      Fate: superseed as eb5a0daa2192
+  |      Fate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg fatelog
   o  eb5a0daa2192
   |
   | @  0dec01379d3b
+  | |    Obsfate: rewritten by test as eb5a0daa2192
   | |
   | x  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
 
@@ -816,20 +862,24 @@
   | @  b7ea6d14e664
   | |    Successors: [eb5a0daa2192]
   | |    semi-colon: [eb5a0daa2192]
-  | |    Fate: superseed as eb5a0daa2192
+  | |    Fate: rewritten by test as eb5a0daa2192
+  | |
   | x  471f378eab4c
   |/     Successors: [eb5a0daa2192]
   |      semi-colon: [eb5a0daa2192]
-  |      Fate: superseed as eb5a0daa2192
+  |      Fate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg fatelog
   o  eb5a0daa2192
   |
   | @  b7ea6d14e664
+  | |    Obsfate: rewritten by test as eb5a0daa2192
   | |
   | x  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
 
@@ -849,38 +899,44 @@
   | |    semi-colon: 0dec01379d3b
   | |    Successors: [eb5a0daa2192]
   | |    semi-colon: [eb5a0daa2192]
-  | |    Fate: superseed as eb5a0daa2192
+  | |    Fate: rewritten by test as eb5a0daa2192
+  | |
   | | x  0dec01379d3b
   | |/     Successors: [b7ea6d14e664]
   | |      semi-colon: [b7ea6d14e664]
-  | |      Fate: superseed as b7ea6d14e664
+  | |      Fate: rewritten by test as b7ea6d14e664
+  | |
   | x  471f378eab4c
   |/     Successors: [eb5a0daa2192]
   |      semi-colon: [eb5a0daa2192]
-  |      Fate: superseed as eb5a0daa2192
+  |      Fate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg fatelog --hidden
   @  eb5a0daa2192
   |
   | x  b7ea6d14e664
+  | |    Obsfate: rewritten by test as eb5a0daa2192
   | |
   | | x  0dec01379d3b
-  | |/
+  | |/     Obsfate: rewritten by test as b7ea6d14e664
+  | |
   | x  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as eb5a0daa2192
+  |
   o  ea207398892e
   
   $ hg fatelogjson --hidden
-  @  eb5a0daa2192
+  @  eb5a0daa2192 ""
   |
-  | x  b7ea6d14e664
+  | x  b7ea6d14e664 [{"markers": [["\udcb7\udceam\u0014\udce6d\udcbd\u0212\"!\udcf7\udc99&1\udcb5\r\udca3\udcfb\u0007", ["\udcebZ\r\udcaa!\udc92;\udcbf\udc8c\udcae\udcb2\udcc4 \udc85\udcb9\udce4c\udc86\u001f\udcd0"], 0, [["ef1", "13"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["\udcebZ\r\udcaa!\udc92;\udcbf\udc8c\udcae\udcb2\udcc4 \udc85\udcb9\udce4c\udc86\u001f\udcd0"], "users": ["test"], "verb": "rewritten"}]
   | |
-  | | x  0dec01379d3b
+  | | x  0dec01379d3b [{"markers": [["\r\udcec\u00017\udc9d;\udce61\udc8cG\u000e\udcad1\udcb1\udcfez\udce7\udccbS\udcd5", ["\udcb7\udceam\u0014\udce6d\udcbd\u0212\"!\udcf7\udc99&1\udcb5\r\udca3\udcfb\u0007"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["\udcb7\udceam\u0014\udce6d\udcbd\u0212\"!\udcf7\udc99&1\udcb5\r\udca3\udcfb\u0007"], "users": ["test"], "verb": "rewritten"}]
   | |/
-  | x  471f378eab4c
+  | x  471f378eab4c [{"markers": [["G\u001f7\udc8e\udcabL^%\udcf6\udcc7\u007fx['\udcc96\udcef\udcb2(t", ["\udcebZ\r\udcaa!\udc92;\udcbf\udc8c\udcae\udcb2\udcc4 \udc85\udcb9\udce4c\udc86\u001f\udcd0"], 0, [["ef1", "9"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["\udcebZ\r\udcaa!\udc92;\udcbf\udc8c\udcae\udcb2\udcc4 \udc85\udcb9\udce4c\udc86\u001f\udcd0"], "users": ["test"], "verb": "rewritten"}]
   |/
-  o  ea207398892e
+  o  ea207398892e ""
   
 
 Test template with pushed and pulled obs markers
@@ -966,7 +1022,6 @@
      date:        Thu Jan 01 00:00:00 1970 +0000
      summary:     ROOT
   
-
 Check templates
 ---------------
 
@@ -977,14 +1032,16 @@
   | @  471f378eab4c
   |/     Successors: [7a230b46bf61]
   |      semi-colon: [7a230b46bf61]
-  |      Fate: superseed as 7a230b46bf61
+  |      Fate: rewritten by test as 7a230b46bf61
+  |
   o  ea207398892e
   
   $ hg fatelog --hidden -v
   o  7a230b46bf61
   |
   | @  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as 7a230b46bf61 (at 1970-01-01 00:00 +0000)
+  |
   o  ea207398892e
   
   $ hg up 'desc(A2)'
@@ -1006,23 +1063,25 @@
   | x  471f378eab4c
   |/     Successors: [7a230b46bf61]
   |      semi-colon: [7a230b46bf61]
-  |      Fate: superseed as 7a230b46bf61
+  |      Fate: rewritten by test as 7a230b46bf61
+  |
   o  ea207398892e
   
   $ hg fatelog --hidden -v
   @  7a230b46bf61
   |
   | x  471f378eab4c
-  |/
+  |/     Obsfate: rewritten by test as 7a230b46bf61 (at 1970-01-01 00:00 +0000)
+  |
   o  ea207398892e
   
 
   $ hg fatelogjson --hidden
-  @  7a230b46bf61
+  @  7a230b46bf61 ""
   |
-  | x  471f378eab4c
+  | x  471f378eab4c [{"markers": [["G\u001f7\udc8e\udcabL^%\udcf6\udcc7\u007fx['\udcc96\udcef\udcb2(t", ["\udcfd\udcf9\udcbd\udce5\u0012\udc9a(\udcd4T\udc8f\udcad\udcd3\udcf6+&\\\udcdd;z."], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null], ["\udcfd\udcf9\udcbd\udce5\u0012\udc9a(\udcd4T\udc8f\udcad\udcd3\udcf6+&\\\udcdd;z.", ["z#\u000bF\udcbfa\udce5\u000b00\udc8cl\udcfd{\udcd1&\udc9e\udcf5G\u0002"], 0, [["ef1", "1"], ["user", "test"]], [0.0, 0], null]], "max_date": [0.0, 0], "min_date": [0.0, 0], "successors": ["z#\u000bF\udcbfa\udce5\u000b00\udc8cl\udcfd{\udcd1&\udc9e\udcf5G\u0002"], "users": ["test"], "verb": "rewritten"}]
   |/
-  o  ea207398892e
+  o  ea207398892e ""
   
 
 Test templates with pruned commits
@@ -1050,10 +1109,12 @@
   $ hg tlog
   @  471f378eab4c
   |    Fate: pruned
+  |
   o  ea207398892e
   
   $ hg fatelog -v
   @  471f378eab4c
+  |    Obsfate: pruned
   |
   o  ea207398892e