Patchwork [7,of,9,V4] template: compute dates in obsfatedate

login
register
mail settings
Submitter Boris Feld
Date Aug. 23, 2017, 3:18 p.m.
Message ID <b8b8b5bd728a5fb9fa84.1503501521@FB>
Download mbox | patch
Permalink /patch/23249/
State Accepted
Headers show

Comments

Boris Feld - Aug. 23, 2017, 3:18 p.m.
# HG changeset patch
# User Boris Feld <boris.feld@octobus.net>
# Date 1499088850 -7200
#      Mon Jul 03 15:34:10 2017 +0200
# Node ID b8b8b5bd728a5fb9fa847d56dd489c77a054b5ee
# Parent  fc257b24c35c492591f348b039d9e3f9488118a8
# EXP-Topic obsfatetemplate
template: compute dates in obsfatedate

Extract the dates from obsmarkers. Compute the min and max date from the
obsmarker range list.
Yuya Nishihara - Aug. 25, 2017, 2:08 p.m.
On Wed, 23 Aug 2017 17:18:41 +0200, Boris Feld wrote:
> # HG changeset patch
> # User Boris Feld <boris.feld@octobus.net>
> # Date 1499088850 -7200
> #      Mon Jul 03 15:34:10 2017 +0200
> # Node ID b8b8b5bd728a5fb9fa847d56dd489c77a054b5ee
> # Parent  fc257b24c35c492591f348b039d9e3f9488118a8
> # EXP-Topic obsfatetemplate
> template: compute dates in obsfatedate
> 
> Extract the dates from obsmarkers. Compute the min and max date from the
> obsmarker range list.
> 
> diff -r fc257b24c35c -r b8b8b5bd728a mercurial/obsutil.py
> --- a/mercurial/obsutil.py	Mon Jul 03 15:34:00 2017 +0200
> +++ b/mercurial/obsutil.py	Mon Jul 03 15:34:10 2017 +0200
> @@ -587,6 +587,11 @@
>  
>      return sorted(users)
>  
> +def _markersdates(markers):
> +    """returns the list of dates for a list of markers
> +    """
> +    return [m[4] for m in markers]
> +
>  def successorsandmarkers(repo, ctx):
>      """compute the raw data needed for computing obsfate
>      Returns a list of dict, one dict per successors set
> diff -r fc257b24c35c -r b8b8b5bd728a mercurial/templater.py
> --- a/mercurial/templater.py	Mon Jul 03 15:34:00 2017 +0200
> +++ b/mercurial/templater.py	Mon Jul 03 15:34:10 2017 +0200
> @@ -888,6 +888,55 @@
>      data = obsutil._markersusers(markers)
>      return templatekw._hybrid(None, data, lambda x: x, lambda d: d)
>  
> +@templatefunc('obsfatedate(markers)')
> +def obsfatedate(context, mapping, args):
> +    """ Compute obsfate related information based on markers
> +
> +    (EXPERIMENTAL)
> +    """
> +    if not len(args) == 1:
> +        # i18n: "obsfatedate" is a keyword
> +        raise error.ParseError(_("obsfatedate expects one arguments"))
> +
> +    markers = evalfuncarg(context, mapping, args[0])
> +    if not isinstance(markers, collections.Iterable):
> +        # i18n: "obsfatedate" is a keyword
> +        errmsg = "obsfatedate first argument should be an iterable"
> +        raise error.ParseError(errmsg)
> +
> +    data = obsutil._markersdates(markers)
> +    return templatekw._hybrid(None, data, lambda x: x, lambda d: d)

Perhaps, hybridlist() can be used.

> +@templatefunc('min(iterable)')
> +def tmplmin(context, mapping, args, **kwargs):

Nit: the other functions are named as if_, dict_, etc., and roughly sorted
alphabetically.

> +    """ Return the min of an iterable
> +    """
> +    if not len(args) == 1:
> +        # i18n: "min" is a keyword
> +        raise error.ParseError(_("min expects one arguments"))
> +
> +    iterable = evalfuncarg(context, mapping, args[0])
> +    if not isinstance(iterable, collections.Iterable):
> +        # i18n: "obsfatedate" is a keyword
                   ^^^^^^^^^^^^^
Outdated comment.

> +        raise error.ParseError(_("min first argument should be an iterable"))
> +
> +    return min(iterable)

An empty list is also invalid. Perhaps we can catch TypeError and
ValueError instead of testing if the argument is an iterable.
Boris Feld - Aug. 28, 2017, 8:06 p.m.
On Fri, 2017-08-25 at 23:08 +0900, Yuya Nishihara wrote:
> On Wed, 23 Aug 2017 17:18:41 +0200, Boris Feld wrote:
> > # HG changeset patch
> > # User Boris Feld <boris.feld@octobus.net>
> > # Date 1499088850 -7200
> > #      Mon Jul 03 15:34:10 2017 +0200
> > # Node ID b8b8b5bd728a5fb9fa847d56dd489c77a054b5ee
> > # Parent  fc257b24c35c492591f348b039d9e3f9488118a8
> > # EXP-Topic obsfatetemplate
> > template: compute dates in obsfatedate
> > 
> > Extract the dates from obsmarkers. Compute the min and max date
> > from the
> > obsmarker range list.
> > 
> > diff -r fc257b24c35c -r b8b8b5bd728a mercurial/obsutil.py
> > --- a/mercurial/obsutil.py	Mon Jul 03 15:34:00 2017 +0200
> > +++ b/mercurial/obsutil.py	Mon Jul 03 15:34:10 2017 +0200
> > @@ -587,6 +587,11 @@
> >  
> >      return sorted(users)
> >  
> > +def _markersdates(markers):
> > +    """returns the list of dates for a list of markers
> > +    """
> > +    return [m[4] for m in markers]
> > +
> >  def successorsandmarkers(repo, ctx):
> >      """compute the raw data needed for computing obsfate
> >      Returns a list of dict, one dict per successors set
> > diff -r fc257b24c35c -r b8b8b5bd728a mercurial/templater.py
> > --- a/mercurial/templater.py	Mon Jul 03 15:34:00 2017 +0200
> > +++ b/mercurial/templater.py	Mon Jul 03 15:34:10 2017 +0200
> > @@ -888,6 +888,55 @@
> >      data = obsutil._markersusers(markers)
> >      return templatekw._hybrid(None, data, lambda x: x, lambda d:
> > d)
> >  
> > +@templatefunc('obsfatedate(markers)')
> > +def obsfatedate(context, mapping, args):
> > +    """ Compute obsfate related information based on markers
> > +
> > +    (EXPERIMENTAL)
> > +    """
> > +    if not len(args) == 1:
> > +        # i18n: "obsfatedate" is a keyword
> > +        raise error.ParseError(_("obsfatedate expects one
> > arguments"))
> > +
> > +    markers = evalfuncarg(context, mapping, args[0])
> > +    if not isinstance(markers, collections.Iterable):
> > +        # i18n: "obsfatedate" is a keyword
> > +        errmsg = "obsfatedate first argument should be an
> > iterable"
> > +        raise error.ParseError(errmsg)
> > +
> > +    data = obsutil._markersdates(markers)
> > +    return templatekw._hybrid(None, data, lambda x: x, lambda d:
> > d)
> 
> Perhaps, hybridlist() can be used.

I have send a V5 series with modifications according to your
recommendations. Thank you very much for your reviews!

I tried using hybrid list in obsfatedate but it seems to crash because
obsmarker date are tuple. The end of the traceback is:

+    File ".../mercurial/mercurial/templatefilters.py", line 360, in
stringify
+      return "".join([stringify(t) for t in thing if t is not None])
+    File ".../mercurial/mercurial/templatekw.py", line 55, in
_defaultgen
+      yield self.joinfmt(d)
+    File ".../mercurial/mercurial/templatekw.py", line 81, in <lambda>
+      return _hybrid(gen, data, lambda x: {name: x}, lambda d: fmt %
d[name])
+  TypeError: not all arguments converted during string formatting
+  [1]

It seems to be because of joinfmt and I'm not sure what would be the
best way to workaround.

> 
> > +@templatefunc('min(iterable)')
> > +def tmplmin(context, mapping, args, **kwargs):
> 
> Nit: the other functions are named as if_, dict_, etc., and roughly
> sorted
> alphabetically.
> 
> > +    """ Return the min of an iterable
> > +    """
> > +    if not len(args) == 1:
> > +        # i18n: "min" is a keyword
> > +        raise error.ParseError(_("min expects one arguments"))
> > +
> > +    iterable = evalfuncarg(context, mapping, args[0])
> > +    if not isinstance(iterable, collections.Iterable):
> > +        # i18n: "obsfatedate" is a keyword
> 
>                    ^^^^^^^^^^^^^
> Outdated comment.
> 
> > +        raise error.ParseError(_("min first argument should be an
> > iterable"))
> > +
> > +    return min(iterable)
> 
> An empty list is also invalid. Perhaps we can catch TypeError and
> ValueError instead of testing if the argument is an iterable.

Patch

diff -r fc257b24c35c -r b8b8b5bd728a mercurial/obsutil.py
--- a/mercurial/obsutil.py	Mon Jul 03 15:34:00 2017 +0200
+++ b/mercurial/obsutil.py	Mon Jul 03 15:34:10 2017 +0200
@@ -587,6 +587,11 @@ 
 
     return sorted(users)
 
+def _markersdates(markers):
+    """returns the list of dates for a list of markers
+    """
+    return [m[4] for m in markers]
+
 def successorsandmarkers(repo, ctx):
     """compute the raw data needed for computing obsfate
     Returns a list of dict, one dict per successors set
diff -r fc257b24c35c -r b8b8b5bd728a mercurial/templater.py
--- a/mercurial/templater.py	Mon Jul 03 15:34:00 2017 +0200
+++ b/mercurial/templater.py	Mon Jul 03 15:34:10 2017 +0200
@@ -888,6 +888,55 @@ 
     data = obsutil._markersusers(markers)
     return templatekw._hybrid(None, data, lambda x: x, lambda d: d)
 
+@templatefunc('obsfatedate(markers)')
+def obsfatedate(context, mapping, args):
+    """ Compute obsfate related information based on markers
+
+    (EXPERIMENTAL)
+    """
+    if not len(args) == 1:
+        # i18n: "obsfatedate" is a keyword
+        raise error.ParseError(_("obsfatedate expects one arguments"))
+
+    markers = evalfuncarg(context, mapping, args[0])
+    if not isinstance(markers, collections.Iterable):
+        # i18n: "obsfatedate" is a keyword
+        errmsg = "obsfatedate first argument should be an iterable"
+        raise error.ParseError(errmsg)
+
+    data = obsutil._markersdates(markers)
+    return templatekw._hybrid(None, data, lambda x: x, lambda d: d)
+
+@templatefunc('min(iterable)')
+def tmplmin(context, mapping, args, **kwargs):
+    """ Return the min of an iterable
+    """
+    if not len(args) == 1:
+        # i18n: "min" is a keyword
+        raise error.ParseError(_("min expects one arguments"))
+
+    iterable = evalfuncarg(context, mapping, args[0])
+    if not isinstance(iterable, collections.Iterable):
+        # i18n: "obsfatedate" is a keyword
+        raise error.ParseError(_("min first argument should be an iterable"))
+
+    return min(iterable)
+
+@templatefunc('max(iterable)')
+def tmplmax(context, mapping, args, **kwargs):
+    """ Return the max of an iterable
+    """
+    if not len(args) == 1:
+        # i18n: "max" is a keyword
+        raise error.ParseError(_("max expects one arguments"))
+
+    iterable = evalfuncarg(context, mapping, args[0])
+    if not isinstance(iterable, collections.Iterable):
+        # i18n: "obsfatedate" is a keyword
+        raise error.ParseError(_("max first argument should be an iterable"))
+
+    return max(iterable)
+
 @templatefunc('relpath(path)')
 def relpath(context, mapping, args):
     """Convert a repository-absolute path into a filesystem path relative to
diff -r fc257b24c35c -r b8b8b5bd728a tests/test-obsmarker-template.t
--- a/tests/test-obsmarker-template.t	Mon Jul 03 15:34:00 2017 +0200
+++ b/tests/test-obsmarker-template.t	Mon Jul 03 15:34:10 2017 +0200
@@ -15,7 +15,8 @@ 
   > obsfatesuccessors = " as {join(successors, ", ")}"
   > obsfateverb = "{obsfateverb(successors)}"
   > obsfateusers = "{if(obsfateusers(markers), " by {join(obsfateusers(markers), ", ")}")}"
-  > obsfate = "{obsfateverb}{obsfatesuccessors}{obsfateusers}; "
+  > 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})")}")}"
+  > obsfate = "{obsfateverb}{obsfatesuccessors}{obsfateusers}{obsfatedate}; "
   > [alias]
   > tlog = log -G -T '{node|short}\
   >     {if(predecessors, "\n  Predecessors: {predecessors}")}\
@@ -94,21 +95,21 @@ 
   o  d004c8f274b9
   |
   | @  471f378eab4c
-  |/     Obsfate: rewritten as 4:d004c8f274b9 by test1, test2;
+  |/     Obsfate: rewritten as 4:d004c8f274b9 by test1, test2 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000);
   o  ea207398892e
   
   $ hg fatelog
   o  d004c8f274b9
   |
   | @  471f378eab4c
-  |/     Obsfate: rewritten as 4:d004c8f274b9 by test1, test2;
+  |/     Obsfate: rewritten as 4:d004c8f274b9 by test1, test2 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000);
   o  ea207398892e
   
   $ hg fatelog -v
   o  d004c8f274b9
   |
   | @  471f378eab4c
-  |/     Obsfate: rewritten as 4:d004c8f274b9 by test1, test2;
+  |/     Obsfate: rewritten as 4:d004c8f274b9 by test1, test2 (between 2001-04-19 04:25 +0000 and 2009-02-13 23:31 +0000);
   o  ea207398892e
   
   $ hg up 'desc(A1)' --hidden
@@ -131,7 +132,7 @@ 
   o  d004c8f274b9
   |
   | @  a468dc9b3633
-  |/     Obsfate: rewritten as 4:d004c8f274b9 by test2;
+  |/     Obsfate: rewritten as 4:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
   o  ea207398892e
   
 Predecessors template should show all the predecessors as we force their display
@@ -162,11 +163,11 @@ 
   o  d004c8f274b9
   |
   | @  a468dc9b3633
-  |/     Obsfate: rewritten as 4:d004c8f274b9 by test2;
+  |/     Obsfate: rewritten as 4:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
   | x  f137d23bb3e1
   | |
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 3:a468dc9b3633 by test1;
+  |/     Obsfate: rewritten as 3:a468dc9b3633 by test1 (at 2009-02-13 23:31 +0000);
   o  ea207398892e
   
 
@@ -211,11 +212,11 @@ 
   @  d004c8f274b9
   |
   | x  a468dc9b3633
-  |/     Obsfate: rewritten as 4:d004c8f274b9 by test2;
+  |/     Obsfate: rewritten as 4:d004c8f274b9 by test2 (at 2001-04-19 04:25 +0000);
   | x  f137d23bb3e1
   | |
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 3:a468dc9b3633 by test1;
+  |/     Obsfate: rewritten as 3:a468dc9b3633 by test1 (at 2009-02-13 23:31 +0000);
   o  ea207398892e
   
   $ hg fatelogjson --hidden
@@ -319,7 +320,7 @@ 
   o  337fec4d2edc
   |
   | @  471597cad322
-  |/     Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test;
+  |/     Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg up f257fde29c7a
@@ -360,7 +361,7 @@ 
   o  337fec4d2edc
   |
   | x  471597cad322
-  |/     Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test;
+  |/     Obsfate: split as 2:337fec4d2edc, 3:f257fde29c7a by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg fatelogjson --hidden
@@ -461,7 +462,7 @@ 
   o  eb5a0daa2192
   |
   | @  471f378eab4c
-  |/     Obsfate: rewritten as 3:eb5a0daa2192 by test;
+  |/     Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg up 'desc(B0)' --hidden
@@ -490,9 +491,9 @@ 
   o  eb5a0daa2192
   |
   | @  0dec01379d3b
-  | |    Obsfate: rewritten as 3:eb5a0daa2192 by test;
+  | |    Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 3:eb5a0daa2192 by test;
+  |/     Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg up 'desc(C0)'
@@ -528,9 +529,9 @@ 
   @  eb5a0daa2192
   |
   | x  0dec01379d3b
-  | |    Obsfate: rewritten as 3:eb5a0daa2192 by test;
+  | |    Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 3:eb5a0daa2192 by test;
+  |/     Obsfate: rewritten as 3:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
 
@@ -667,7 +668,7 @@ 
   | o  fdf9bde5129a
   |/
   | @  471f378eab4c
-  |/     Obsfate: rewritten as 2:fdf9bde5129a by test; rewritten as 4:019fadeab383 by test;
+  |/     Obsfate: rewritten as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg up 'desc(A1)'
@@ -723,11 +724,11 @@ 
   o  019fadeab383
   |
   | x  65b757b745b9
-  |/     Obsfate: rewritten as 4:019fadeab383 by test;
+  |/     Obsfate: rewritten as 4:019fadeab383 by test (at 1970-01-01 00:00 +0000);
   | @  fdf9bde5129a
   |/
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 2:fdf9bde5129a by test; rewritten as 3:65b757b745b9 by test;
+  |/     Obsfate: rewritten as 2:fdf9bde5129a by test (at 1970-01-01 00:00 +0000); rewritten as 3:65b757b745b9 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
 
@@ -845,7 +846,7 @@ 
   o  eb5a0daa2192
   |
   | @  471f378eab4c
-  |/     Obsfate: rewritten as 4:eb5a0daa2192 by test;
+  |/     Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg up 'desc(B0)' --hidden
@@ -873,9 +874,9 @@ 
   o  eb5a0daa2192
   |
   | @  0dec01379d3b
-  | |    Obsfate: rewritten as 4:eb5a0daa2192 by test;
+  | |    Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 4:eb5a0daa2192 by test;
+  |/     Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg up 'desc(B1)' --hidden
@@ -903,9 +904,9 @@ 
   o  eb5a0daa2192
   |
   | @  b7ea6d14e664
-  | |    Obsfate: rewritten as 4:eb5a0daa2192 by test;
+  | |    Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 4:eb5a0daa2192 by test;
+  |/     Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg up 'desc(C0)'
@@ -954,11 +955,11 @@ 
   @  eb5a0daa2192
   |
   | x  b7ea6d14e664
-  | |    Obsfate: rewritten as 4:eb5a0daa2192 by test;
+  | |    Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   | | x  0dec01379d3b
-  | |/     Obsfate: rewritten as 3:b7ea6d14e664 by test;
+  | |/     Obsfate: rewritten as 3:b7ea6d14e664 by test (at 1970-01-01 00:00 +0000);
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 4:eb5a0daa2192 by test;
+  |/     Obsfate: rewritten as 4:eb5a0daa2192 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
 
@@ -1082,7 +1083,7 @@ 
   o  7a230b46bf61
   |
   | @  471f378eab4c
-  |/     Obsfate: rewritten as 2:7a230b46bf61 by test;
+  |/     Obsfate: rewritten as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg up 'desc(A2)'
@@ -1119,7 +1120,7 @@ 
   @  7a230b46bf61
   |
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 2:7a230b46bf61 by test;
+  |/     Obsfate: rewritten as 2:7a230b46bf61 by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
 
@@ -1194,9 +1195,9 @@ 
   o  f897c6137566
   |
   | @  0dec01379d3b
-  | |    Obsfate: rewritten as 3:f897c6137566 by test; rewritten as 1:471f378eab4c by test;
+  | |    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);
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 2:0dec01379d3b by test;
+  |/     Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
 
@@ -1452,7 +1453,7 @@ 
   | o  ba2ed02b0c9a
   | |
   | x  4a004186e638
-  |/     Obsfate: rewritten as 8:b18bc8331526 by test; rewritten as 9:0b997eb7ceee by test;
+  |/     Obsfate: rewritten as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000); rewritten as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000);
   o  dd800401bd8c
   |
   o  f897c6137566
@@ -1525,17 +1526,17 @@ 
   | o  ba2ed02b0c9a
   | |
   | x  4a004186e638
-  |/     Obsfate: rewritten as 8:b18bc8331526 by test; rewritten as 9:0b997eb7ceee by test;
+  |/     Obsfate: rewritten as 8:b18bc8331526 by test (at 1970-01-01 00:00 +0000); rewritten as 9:0b997eb7ceee by test (at 1970-01-01 00:00 +0000);
   o  dd800401bd8c
   |
   | x  9bd10a0775e4
-  |/     Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a by test;
+  |/     Obsfate: split as 5:dd800401bd8c, 6:4a004186e638, 7:ba2ed02b0c9a by test (at 1970-01-01 00:00 +0000);
   o  f897c6137566
   |
   | x  0dec01379d3b
-  | |    Obsfate: rewritten as 3:f897c6137566 by test; rewritten as 1:471f378eab4c by test;
+  | |    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);
   | x  471f378eab4c
-  |/     Obsfate: rewritten as 2:0dec01379d3b by test;
+  |/     Obsfate: rewritten as 2:0dec01379d3b by test (at 1970-01-01 00:00 +0000);
   o  ea207398892e
   
   $ hg fatelogjson --hidden
@@ -1604,7 +1605,7 @@ 
   o  dd800401bd8c
   |
   | @  9bd10a0775e4
-  |/     Obsfate: split as 5:dd800401bd8c, 9:0b997eb7ceee, 10:eceed8f98ffc by test; split as 5:dd800401bd8c, 8:b18bc8331526, 10:eceed8f98ffc by test;
+  |/     Obsfate: split as 5:dd800401bd8c, 9:0b997eb7ceee, 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000); split as 5:dd800401bd8c, 8:b18bc8331526, 10:eceed8f98ffc by test (at 1970-01-01 00:00 +0000);
   o  f897c6137566
   |
   o  ea207398892e