Patchwork [V2] graphmod: partial edge styling

login
register
mail settings
Submitter Martijn Pieters
Date May 4, 2016, 7:23 p.m.
Message ID <b642321339fdc0277b67.1462389803@mjpieters-mbp>
Download mbox | patch
Permalink /patch/14886/
State Accepted
Headers show

Comments

Martijn Pieters - May 4, 2016, 7:23 p.m.
# HG changeset patch
# User Martijn Pieters <mjpieters@fb.com>
# Date 1462389119 -3600
#      Wed May 04 20:11:59 2016 +0100
# Node ID b642321339fdc0277b67227809c90341c64c2df5
# Parent  9fb2e8c8f3204a610eab3576d6aaf2fa7b8bc450
graphmod: partial edge styling

Allow for a style to only apply to the last N lines (for positive N) or
everything but the first N lines (for negative N) of the section along the
current node. This allows for more subtle grandparent styling.

So from the default:

  $ hg log -G ...
  o    Lorem ipsum dolor sit
  :\   amet, consectetur
  : :  adipiscing elit, sed
  : :  do eiusmod tempor
  : :
  o :  incididunt ut labore
  | :  et dolore magna
  | :  aliqua. Ut enim ad
  | :  minim veniam, quis
  |/
  o  nostrud exercitation
  :  ullamco laboris nisi
  :  ut aliquip ex ea
  :  commodo consequat.
  :
  o  Duis aute irure dolor
  |  in reprehenderit in
  ~  voluptate velit esse
     cillum dolore eu

to

  $ hg log -G --config "experimental.graphstyle.grandparent=2." ...
  o    Lorem ipsum dolor sit
  |\   amet, consectetur
  | |  adipiscing elit, sed
  . .  do eiusmod tempor
  . .
  o |  incididunt ut labore
  | |  et dolore magna
  | |  aliqua. Ut enim ad
  | |  minim veniam, quis
  |/
  o  nostrud exercitation
  |  ullamco laboris nisi
  |  ut aliquip ex ea
  .  commodo consequat.
  .
  o  Duis aute irure dolor
  |  in reprehenderit in
  ~  voluptate velit esse
     cillum dolore eu

or

  $ hg log -G --config "experimental.graphstyle.grandparent=1:" ...
  o    Lorem ipsum dolor sit
  |\   amet, consectetur
  | |  adipiscing elit, sed
  | |  do eiusmod tempor
  : :
  o |  incididunt ut labore
  | |  et dolore magna
  | |  aliqua. Ut enim ad
  | |  minim veniam, quis
  |/
  o  nostrud exercitation
  |  ullamco laboris nisi
  |  ut aliquip ex ea
  |  commodo consequat.
  :
  o  Duis aute irure dolor
  |  in reprehenderit in
  ~  voluptate velit esse
     cillum dolore eu

or

  $ hg log -G --config "experimental.graphstyle.grandparent=-2!" ...
  o    Lorem ipsum dolor sit
  |\   amet, consectetur
  ! !  adipiscing elit, sed
  ! !  do eiusmod tempor
  ! !
  o |  incididunt ut labore
  | |  et dolore magna
  | |  aliqua. Ut enim ad
  | |  minim veniam, quis
  |/
  o  nostrud exercitation
  |  ullamco laboris nisi
  !  ut aliquip ex ea
  !  commodo consequat.
  !
  o  Duis aute irure dolor
  |  in reprehenderit in
  ~  voluptate velit esse
     cillum dolore eu
Augie Fackler - May 11, 2016, 1:42 a.m.
On Wed, May 04, 2016 at 08:23:23PM +0100, Martijn Pieters wrote:
> # HG changeset patch
> # User Martijn Pieters <mjpieters@fb.com>
> # Date 1462389119 -3600
> #      Wed May 04 20:11:59 2016 +0100
> # Node ID b642321339fdc0277b67227809c90341c64c2df5
> # Parent  9fb2e8c8f3204a610eab3576d6aaf2fa7b8bc450
> graphmod: partial edge styling

Nice, queued.

>
> Allow for a style to only apply to the last N lines (for positive N) or
> everything but the first N lines (for negative N) of the section along the
> current node. This allows for more subtle grandparent styling.
>
> So from the default:
>
>   $ hg log -G ...
>   o    Lorem ipsum dolor sit
>   :\   amet, consectetur
>   : :  adipiscing elit, sed
>   : :  do eiusmod tempor
>   : :
>   o :  incididunt ut labore
>   | :  et dolore magna
>   | :  aliqua. Ut enim ad
>   | :  minim veniam, quis
>   |/
>   o  nostrud exercitation
>   :  ullamco laboris nisi
>   :  ut aliquip ex ea
>   :  commodo consequat.
>   :
>   o  Duis aute irure dolor
>   |  in reprehenderit in
>   ~  voluptate velit esse
>      cillum dolore eu
>
> to
>
>   $ hg log -G --config "experimental.graphstyle.grandparent=2." ...
>   o    Lorem ipsum dolor sit
>   |\   amet, consectetur
>   | |  adipiscing elit, sed
>   . .  do eiusmod tempor
>   . .
>   o |  incididunt ut labore
>   | |  et dolore magna
>   | |  aliqua. Ut enim ad
>   | |  minim veniam, quis
>   |/
>   o  nostrud exercitation
>   |  ullamco laboris nisi
>   |  ut aliquip ex ea
>   .  commodo consequat.
>   .
>   o  Duis aute irure dolor
>   |  in reprehenderit in
>   ~  voluptate velit esse
>      cillum dolore eu
>
> or
>
>   $ hg log -G --config "experimental.graphstyle.grandparent=1:" ...
>   o    Lorem ipsum dolor sit
>   |\   amet, consectetur
>   | |  adipiscing elit, sed
>   | |  do eiusmod tempor
>   : :
>   o |  incididunt ut labore
>   | |  et dolore magna
>   | |  aliqua. Ut enim ad
>   | |  minim veniam, quis
>   |/
>   o  nostrud exercitation
>   |  ullamco laboris nisi
>   |  ut aliquip ex ea
>   |  commodo consequat.
>   :
>   o  Duis aute irure dolor
>   |  in reprehenderit in
>   ~  voluptate velit esse
>      cillum dolore eu
>
> or
>
>   $ hg log -G --config "experimental.graphstyle.grandparent=-2!" ...
>   o    Lorem ipsum dolor sit
>   |\   amet, consectetur
>   ! !  adipiscing elit, sed
>   ! !  do eiusmod tempor
>   ! !
>   o |  incididunt ut labore
>   | |  et dolore magna
>   | |  aliqua. Ut enim ad
>   | |  minim veniam, quis
>   |/
>   o  nostrud exercitation
>   |  ullamco laboris nisi
>   !  ut aliquip ex ea
>   !  commodo consequat.
>   !
>   o  Duis aute irure dolor
>   |  in reprehenderit in
>   ~  voluptate velit esse
>      cillum dolore eu
>
> diff --git a/mercurial/graphmod.py b/mercurial/graphmod.py
> --- a/mercurial/graphmod.py
> +++ b/mercurial/graphmod.py
> @@ -32,7 +32,9 @@
>  GRANDPARENT = 'G'
>  MISSINGPARENT = 'M'
>  # Style of line to draw. None signals a line that ends and is removed at this
> -# point.
> +# point. A number prefix means only the last N characters of the current block
> +# will use that style, the rest will use the PARENT style. Add a - sign
> +# (so making N negative) and all but the first N characters use that style.
>  EDGES = {PARENT: '|', GRANDPARENT: ':', MISSINGPARENT: None}
>
>  def groupbranchiter(revs, parentsfunc, firstbranch=()):
> @@ -653,6 +655,22 @@
>      while len(text) < len(lines):
>          text.append("")
>
> +    if any(len(char) > 1 for char in edgemap.values()):
> +        # limit drawing an edge to the first or last N lines of the current
> +        # section the rest of the edge is drawn like a parent line.
> +        parent = state['styles'][PARENT][-1]
> +        def _drawgp(char, i):
> +            # should a grandparent character be drawn for this line?
> +            if len(char) < 2:
> +                return True
> +            num = int(char[:-1])
> +            # either skip first num lines or take last num lines, based on sign
> +            return -num <= i if num < 0 else (len(lines) - i) <= num
> +        for i, line in enumerate(lines):
> +            line[:] = [c[-1] if _drawgp(c, i) else parent for c in line]
> +        edgemap = dict(
> +            (e, c if len(c) < 2 else parent) for e, c in edgemap.items())
> +
>      # print lines
>      indentation_level = max(ncols, ncols + coldiff)
>      for (line, logstr) in zip(lines, text):
> diff --git a/tests/test-glog.t b/tests/test-glog.t
> --- a/tests/test-glog.t
> +++ b/tests/test-glog.t
> @@ -2632,7 +2632,229 @@
>         date:        Thu Jan 01 00:00:04 1970 +0000
>         summary:     (4) merge two known; one immediate left, one immediate right
>
> +Draw only part of a grandparent line differently with "<N><char>"; only the
> +last N lines (for positive N) or everything but the first N lines (for
> +negative N) along the current node use the style, the rest of the edge uses
> +the parent edge styling.
>
> +Last 3 lines:
> +
> +  $ cat << EOF >> $HGRCPATH
> +  > [experimental]
> +  > graphstyle.parent = !
> +  > graphstyle.grandparent = 3.
> +  > graphstyle.missing =
> +  > EOF
> +  $ hg log -G -r '36:18 & file("a")' -m
> +  @  changeset:   36:08a19a744424
> +  !  branch:      branch
> +  !  tag:         tip
> +  !  parent:      35:9159c3644c5e
> +  !  parent:      35:9159c3644c5e
> +  !  user:        test
> +  .  date:        Thu Jan 01 00:00:36 1970 +0000
> +  .  summary:     (36) buggy merge: identical parents
> +  .
> +  o    changeset:   32:d06dffa21a31
> +  !\   parent:      27:886ed638191b
> +  ! !  parent:      31:621d83e11f67
> +  ! !  user:        test
> +  ! .  date:        Thu Jan 01 00:00:32 1970 +0000
> +  ! .  summary:     (32) expand
> +  ! .
> +  o !  changeset:   31:621d83e11f67
> +  !\!  parent:      21:d42a756af44d
> +  ! !  parent:      30:6e11cd4b648f
> +  ! !  user:        test
> +  ! .  date:        Thu Jan 01 00:00:31 1970 +0000
> +  ! .  summary:     (31) expand
> +  ! .
> +  o !    changeset:   30:6e11cd4b648f
> +  !\ \   parent:      28:44ecd0b9ae99
> +  ! ~ !  parent:      29:cd9bb2be7593
> +  !   !  user:        test
> +  !   .  date:        Thu Jan 01 00:00:30 1970 +0000
> +  !   .  summary:     (30) expand
> +  !  /
> +  o !    changeset:   28:44ecd0b9ae99
> +  !\ \   parent:      1:6db2ef61d156
> +  ! ~ !  parent:      26:7f25b6c2f0b9
> +  !   !  user:        test
> +  !   .  date:        Thu Jan 01 00:00:28 1970 +0000
> +  !   .  summary:     (28) merge zero known
> +  !  /
> +  o !    changeset:   26:7f25b6c2f0b9
> +  !\ \   parent:      18:1aa84d96232a
> +  ! ! !  parent:      25:91da8ed57247
> +  ! ! !  user:        test
> +  ! ! .  date:        Thu Jan 01 00:00:26 1970 +0000
> +  ! ! .  summary:     (26) merge one known; far right
> +  ! ! .
> +  ! o !  changeset:   25:91da8ed57247
> +  ! !\!  parent:      21:d42a756af44d
> +  ! ! !  parent:      24:a9c19a3d96b7
> +  ! ! !  user:        test
> +  ! ! .  date:        Thu Jan 01 00:00:25 1970 +0000
> +  ! ! .  summary:     (25) merge one known; far left
> +  ! ! .
> +  ! o !    changeset:   24:a9c19a3d96b7
> +  ! !\ \   parent:      0:e6eb3150255d
> +  ! ! ~ !  parent:      23:a01cddf0766d
> +  ! !   !  user:        test
> +  ! !   .  date:        Thu Jan 01 00:00:24 1970 +0000
> +  ! !   .  summary:     (24) merge one known; immediate right
> +  ! !  /
> +  ! o !    changeset:   23:a01cddf0766d
> +  ! !\ \   parent:      1:6db2ef61d156
> +  ! ! ~ !  parent:      22:e0d9cccacb5d
> +  ! !   !  user:        test
> +  ! !   .  date:        Thu Jan 01 00:00:23 1970 +0000
> +  ! !   .  summary:     (23) merge one known; immediate left
> +  ! !  /
> +  ! o !  changeset:   22:e0d9cccacb5d
> +  !/!/   parent:      18:1aa84d96232a
> +  ! !    parent:      21:d42a756af44d
> +  ! !    user:        test
> +  ! .    date:        Thu Jan 01 00:00:22 1970 +0000
> +  ! .    summary:     (22) merge two known; one far left, one far right
> +  ! .
> +  ! o    changeset:   21:d42a756af44d
> +  ! !\   parent:      19:31ddc2c1573b
> +  ! ! !  parent:      20:d30ed6450e32
> +  ! ! !  user:        test
> +  ! ! !  date:        Thu Jan 01 00:00:21 1970 +0000
> +  ! ! !  summary:     (21) expand
> +  ! ! !
> +  +---o  changeset:   20:d30ed6450e32
> +  ! ! |  parent:      0:e6eb3150255d
> +  ! ! ~  parent:      18:1aa84d96232a
> +  ! !    user:        test
> +  ! !    date:        Thu Jan 01 00:00:20 1970 +0000
> +  ! !    summary:     (20) merge two known; two far right
> +  ! !
> +  ! o    changeset:   19:31ddc2c1573b
> +  ! |\   parent:      15:1dda3f72782d
> +  ! ~ ~  parent:      17:44765d7c06e0
> +  !      user:        test
> +  !      date:        Thu Jan 01 00:00:19 1970 +0000
> +  !      summary:     (19) expand
> +  !
> +  o    changeset:   18:1aa84d96232a
> +  |\   parent:      1:6db2ef61d156
> +  ~ ~  parent:      15:1dda3f72782d
> +       user:        test
> +       date:        Thu Jan 01 00:00:18 1970 +0000
> +       summary:     (18) merge two known; two far left
> +
> +All but the first 3 lines:
> +
> +  $ cat << EOF >> $HGRCPATH
> +  > [experimental]
> +  > graphstyle.parent = !
> +  > graphstyle.grandparent = -3.
> +  > graphstyle.missing =
> +  > EOF
> +  $ hg log -G -r '36:18 & file("a")' -m
> +  @  changeset:   36:08a19a744424
> +  !  branch:      branch
> +  !  tag:         tip
> +  .  parent:      35:9159c3644c5e
> +  .  parent:      35:9159c3644c5e
> +  .  user:        test
> +  .  date:        Thu Jan 01 00:00:36 1970 +0000
> +  .  summary:     (36) buggy merge: identical parents
> +  .
> +  o    changeset:   32:d06dffa21a31
> +  !\   parent:      27:886ed638191b
> +  ! !  parent:      31:621d83e11f67
> +  ! .  user:        test
> +  ! .  date:        Thu Jan 01 00:00:32 1970 +0000
> +  ! .  summary:     (32) expand
> +  ! .
> +  o !  changeset:   31:621d83e11f67
> +  !\!  parent:      21:d42a756af44d
> +  ! !  parent:      30:6e11cd4b648f
> +  ! .  user:        test
> +  ! .  date:        Thu Jan 01 00:00:31 1970 +0000
> +  ! .  summary:     (31) expand
> +  ! .
> +  o !    changeset:   30:6e11cd4b648f
> +  !\ \   parent:      28:44ecd0b9ae99
> +  ! ~ !  parent:      29:cd9bb2be7593
> +  !   .  user:        test
> +  !   .  date:        Thu Jan 01 00:00:30 1970 +0000
> +  !   .  summary:     (30) expand
> +  !  /
> +  o !    changeset:   28:44ecd0b9ae99
> +  !\ \   parent:      1:6db2ef61d156
> +  ! ~ !  parent:      26:7f25b6c2f0b9
> +  !   .  user:        test
> +  !   .  date:        Thu Jan 01 00:00:28 1970 +0000
> +  !   .  summary:     (28) merge zero known
> +  !  /
> +  o !    changeset:   26:7f25b6c2f0b9
> +  !\ \   parent:      18:1aa84d96232a
> +  ! ! !  parent:      25:91da8ed57247
> +  ! ! .  user:        test
> +  ! ! .  date:        Thu Jan 01 00:00:26 1970 +0000
> +  ! ! .  summary:     (26) merge one known; far right
> +  ! ! .
> +  ! o !  changeset:   25:91da8ed57247
> +  ! !\!  parent:      21:d42a756af44d
> +  ! ! !  parent:      24:a9c19a3d96b7
> +  ! ! .  user:        test
> +  ! ! .  date:        Thu Jan 01 00:00:25 1970 +0000
> +  ! ! .  summary:     (25) merge one known; far left
> +  ! ! .
> +  ! o !    changeset:   24:a9c19a3d96b7
> +  ! !\ \   parent:      0:e6eb3150255d
> +  ! ! ~ !  parent:      23:a01cddf0766d
> +  ! !   .  user:        test
> +  ! !   .  date:        Thu Jan 01 00:00:24 1970 +0000
> +  ! !   .  summary:     (24) merge one known; immediate right
> +  ! !  /
> +  ! o !    changeset:   23:a01cddf0766d
> +  ! !\ \   parent:      1:6db2ef61d156
> +  ! ! ~ !  parent:      22:e0d9cccacb5d
> +  ! !   .  user:        test
> +  ! !   .  date:        Thu Jan 01 00:00:23 1970 +0000
> +  ! !   .  summary:     (23) merge one known; immediate left
> +  ! !  /
> +  ! o !  changeset:   22:e0d9cccacb5d
> +  !/!/   parent:      18:1aa84d96232a
> +  ! !    parent:      21:d42a756af44d
> +  ! .    user:        test
> +  ! .    date:        Thu Jan 01 00:00:22 1970 +0000
> +  ! .    summary:     (22) merge two known; one far left, one far right
> +  ! .
> +  ! o    changeset:   21:d42a756af44d
> +  ! !\   parent:      19:31ddc2c1573b
> +  ! ! !  parent:      20:d30ed6450e32
> +  ! ! !  user:        test
> +  ! ! !  date:        Thu Jan 01 00:00:21 1970 +0000
> +  ! ! !  summary:     (21) expand
> +  ! ! !
> +  +---o  changeset:   20:d30ed6450e32
> +  ! ! |  parent:      0:e6eb3150255d
> +  ! ! ~  parent:      18:1aa84d96232a
> +  ! !    user:        test
> +  ! !    date:        Thu Jan 01 00:00:20 1970 +0000
> +  ! !    summary:     (20) merge two known; two far right
> +  ! !
> +  ! o    changeset:   19:31ddc2c1573b
> +  ! |\   parent:      15:1dda3f72782d
> +  ! ~ ~  parent:      17:44765d7c06e0
> +  !      user:        test
> +  !      date:        Thu Jan 01 00:00:19 1970 +0000
> +  !      summary:     (19) expand
> +  !
> +  o    changeset:   18:1aa84d96232a
> +  |\   parent:      1:6db2ef61d156
> +  ~ ~  parent:      15:1dda3f72782d
> +       user:        test
> +       date:        Thu Jan 01 00:00:18 1970 +0000
> +       summary:     (18) merge two known; two far left
> +
>    $ cd ..
>
>  Change graph shorten, test better with graphstyle.missing not none
> _______________________________________________
> Mercurial-devel mailing list
> Mercurial-devel@mercurial-scm.org
> https://www.mercurial-scm.org/mailman/listinfo/mercurial-devel
Yuya Nishihara - May 15, 2016, 10:18 a.m.
On Wed, 04 May 2016 20:23:23 +0100, Martijn Pieters wrote:
> # HG changeset patch
> # User Martijn Pieters <mjpieters@fb.com>
> # Date 1462389119 -3600
> #      Wed May 04 20:11:59 2016 +0100
> # Node ID b642321339fdc0277b67227809c90341c64c2df5
> # Parent  9fb2e8c8f3204a610eab3576d6aaf2fa7b8bc450
> graphmod: partial edge styling

> diff --git a/mercurial/graphmod.py b/mercurial/graphmod.py
> --- a/mercurial/graphmod.py
> +++ b/mercurial/graphmod.py
> @@ -32,7 +32,9 @@
>  GRANDPARENT = 'G'
>  MISSINGPARENT = 'M'
>  # Style of line to draw. None signals a line that ends and is removed at this
> -# point.
> +# point. A number prefix means only the last N characters of the current block
> +# will use that style, the rest will use the PARENT style. Add a - sign
> +# (so making N negative) and all but the first N characters use that style.
>  EDGES = {PARENT: '|', GRANDPARENT: ':', MISSINGPARENT: None}
>  
>  def groupbranchiter(revs, parentsfunc, firstbranch=()):
> @@ -653,6 +655,22 @@
>      while len(text) < len(lines):
>          text.append("")
>  
> +    if any(len(char) > 1 for char in edgemap.values()):
> +        # limit drawing an edge to the first or last N lines of the current
> +        # section the rest of the edge is drawn like a parent line.
> +        parent = state['styles'][PARENT][-1]
> +        def _drawgp(char, i):
> +            # should a grandparent character be drawn for this line?
> +            if len(char) < 2:
> +                return True
> +            num = int(char[:-1])
> +            # either skip first num lines or take last num lines, based on sign
> +            return -num <= i if num < 0 else (len(lines) - i) <= num
> +        for i, line in enumerate(lines):
> +            line[:] = [c[-1] if _drawgp(c, i) else parent for c in line]
> +        edgemap = dict(
> +            (e, c if len(c) < 2 else parent) for e, c in edgemap.items())

It appears new edgemap isn't used at all. Maybe it have to be copied back
to state['edges'] ?
Martijn Pieters - May 16, 2016, 3:08 p.m.
On 15 May 2016 at 11:18, Yuya Nishihara <yuya@tcha.org> wrote:
> It appears new edgemap isn't used at all. Maybe it have to be copied back
> to state['edges'] ?

Ick, yes, the dict should be updated in-place. Patch coming.

Patch

diff --git a/mercurial/graphmod.py b/mercurial/graphmod.py
--- a/mercurial/graphmod.py
+++ b/mercurial/graphmod.py
@@ -32,7 +32,9 @@ 
 GRANDPARENT = 'G'
 MISSINGPARENT = 'M'
 # Style of line to draw. None signals a line that ends and is removed at this
-# point.
+# point. A number prefix means only the last N characters of the current block
+# will use that style, the rest will use the PARENT style. Add a - sign
+# (so making N negative) and all but the first N characters use that style.
 EDGES = {PARENT: '|', GRANDPARENT: ':', MISSINGPARENT: None}
 
 def groupbranchiter(revs, parentsfunc, firstbranch=()):
@@ -653,6 +655,22 @@ 
     while len(text) < len(lines):
         text.append("")
 
+    if any(len(char) > 1 for char in edgemap.values()):
+        # limit drawing an edge to the first or last N lines of the current
+        # section the rest of the edge is drawn like a parent line.
+        parent = state['styles'][PARENT][-1]
+        def _drawgp(char, i):
+            # should a grandparent character be drawn for this line?
+            if len(char) < 2:
+                return True
+            num = int(char[:-1])
+            # either skip first num lines or take last num lines, based on sign
+            return -num <= i if num < 0 else (len(lines) - i) <= num
+        for i, line in enumerate(lines):
+            line[:] = [c[-1] if _drawgp(c, i) else parent for c in line]
+        edgemap = dict(
+            (e, c if len(c) < 2 else parent) for e, c in edgemap.items())
+
     # print lines
     indentation_level = max(ncols, ncols + coldiff)
     for (line, logstr) in zip(lines, text):
diff --git a/tests/test-glog.t b/tests/test-glog.t
--- a/tests/test-glog.t
+++ b/tests/test-glog.t
@@ -2632,7 +2632,229 @@ 
        date:        Thu Jan 01 00:00:04 1970 +0000
        summary:     (4) merge two known; one immediate left, one immediate right
   
+Draw only part of a grandparent line differently with "<N><char>"; only the
+last N lines (for positive N) or everything but the first N lines (for
+negative N) along the current node use the style, the rest of the edge uses
+the parent edge styling.
 
+Last 3 lines:
+
+  $ cat << EOF >> $HGRCPATH
+  > [experimental]
+  > graphstyle.parent = !
+  > graphstyle.grandparent = 3.
+  > graphstyle.missing =
+  > EOF
+  $ hg log -G -r '36:18 & file("a")' -m
+  @  changeset:   36:08a19a744424
+  !  branch:      branch
+  !  tag:         tip
+  !  parent:      35:9159c3644c5e
+  !  parent:      35:9159c3644c5e
+  !  user:        test
+  .  date:        Thu Jan 01 00:00:36 1970 +0000
+  .  summary:     (36) buggy merge: identical parents
+  .
+  o    changeset:   32:d06dffa21a31
+  !\   parent:      27:886ed638191b
+  ! !  parent:      31:621d83e11f67
+  ! !  user:        test
+  ! .  date:        Thu Jan 01 00:00:32 1970 +0000
+  ! .  summary:     (32) expand
+  ! .
+  o !  changeset:   31:621d83e11f67
+  !\!  parent:      21:d42a756af44d
+  ! !  parent:      30:6e11cd4b648f
+  ! !  user:        test
+  ! .  date:        Thu Jan 01 00:00:31 1970 +0000
+  ! .  summary:     (31) expand
+  ! .
+  o !    changeset:   30:6e11cd4b648f
+  !\ \   parent:      28:44ecd0b9ae99
+  ! ~ !  parent:      29:cd9bb2be7593
+  !   !  user:        test
+  !   .  date:        Thu Jan 01 00:00:30 1970 +0000
+  !   .  summary:     (30) expand
+  !  /
+  o !    changeset:   28:44ecd0b9ae99
+  !\ \   parent:      1:6db2ef61d156
+  ! ~ !  parent:      26:7f25b6c2f0b9
+  !   !  user:        test
+  !   .  date:        Thu Jan 01 00:00:28 1970 +0000
+  !   .  summary:     (28) merge zero known
+  !  /
+  o !    changeset:   26:7f25b6c2f0b9
+  !\ \   parent:      18:1aa84d96232a
+  ! ! !  parent:      25:91da8ed57247
+  ! ! !  user:        test
+  ! ! .  date:        Thu Jan 01 00:00:26 1970 +0000
+  ! ! .  summary:     (26) merge one known; far right
+  ! ! .
+  ! o !  changeset:   25:91da8ed57247
+  ! !\!  parent:      21:d42a756af44d
+  ! ! !  parent:      24:a9c19a3d96b7
+  ! ! !  user:        test
+  ! ! .  date:        Thu Jan 01 00:00:25 1970 +0000
+  ! ! .  summary:     (25) merge one known; far left
+  ! ! .
+  ! o !    changeset:   24:a9c19a3d96b7
+  ! !\ \   parent:      0:e6eb3150255d
+  ! ! ~ !  parent:      23:a01cddf0766d
+  ! !   !  user:        test
+  ! !   .  date:        Thu Jan 01 00:00:24 1970 +0000
+  ! !   .  summary:     (24) merge one known; immediate right
+  ! !  /
+  ! o !    changeset:   23:a01cddf0766d
+  ! !\ \   parent:      1:6db2ef61d156
+  ! ! ~ !  parent:      22:e0d9cccacb5d
+  ! !   !  user:        test
+  ! !   .  date:        Thu Jan 01 00:00:23 1970 +0000
+  ! !   .  summary:     (23) merge one known; immediate left
+  ! !  /
+  ! o !  changeset:   22:e0d9cccacb5d
+  !/!/   parent:      18:1aa84d96232a
+  ! !    parent:      21:d42a756af44d
+  ! !    user:        test
+  ! .    date:        Thu Jan 01 00:00:22 1970 +0000
+  ! .    summary:     (22) merge two known; one far left, one far right
+  ! .
+  ! o    changeset:   21:d42a756af44d
+  ! !\   parent:      19:31ddc2c1573b
+  ! ! !  parent:      20:d30ed6450e32
+  ! ! !  user:        test
+  ! ! !  date:        Thu Jan 01 00:00:21 1970 +0000
+  ! ! !  summary:     (21) expand
+  ! ! !
+  +---o  changeset:   20:d30ed6450e32
+  ! ! |  parent:      0:e6eb3150255d
+  ! ! ~  parent:      18:1aa84d96232a
+  ! !    user:        test
+  ! !    date:        Thu Jan 01 00:00:20 1970 +0000
+  ! !    summary:     (20) merge two known; two far right
+  ! !
+  ! o    changeset:   19:31ddc2c1573b
+  ! |\   parent:      15:1dda3f72782d
+  ! ~ ~  parent:      17:44765d7c06e0
+  !      user:        test
+  !      date:        Thu Jan 01 00:00:19 1970 +0000
+  !      summary:     (19) expand
+  !
+  o    changeset:   18:1aa84d96232a
+  |\   parent:      1:6db2ef61d156
+  ~ ~  parent:      15:1dda3f72782d
+       user:        test
+       date:        Thu Jan 01 00:00:18 1970 +0000
+       summary:     (18) merge two known; two far left
+  
+All but the first 3 lines:
+
+  $ cat << EOF >> $HGRCPATH
+  > [experimental]
+  > graphstyle.parent = !
+  > graphstyle.grandparent = -3.
+  > graphstyle.missing =
+  > EOF
+  $ hg log -G -r '36:18 & file("a")' -m
+  @  changeset:   36:08a19a744424
+  !  branch:      branch
+  !  tag:         tip
+  .  parent:      35:9159c3644c5e
+  .  parent:      35:9159c3644c5e
+  .  user:        test
+  .  date:        Thu Jan 01 00:00:36 1970 +0000
+  .  summary:     (36) buggy merge: identical parents
+  .
+  o    changeset:   32:d06dffa21a31
+  !\   parent:      27:886ed638191b
+  ! !  parent:      31:621d83e11f67
+  ! .  user:        test
+  ! .  date:        Thu Jan 01 00:00:32 1970 +0000
+  ! .  summary:     (32) expand
+  ! .
+  o !  changeset:   31:621d83e11f67
+  !\!  parent:      21:d42a756af44d
+  ! !  parent:      30:6e11cd4b648f
+  ! .  user:        test
+  ! .  date:        Thu Jan 01 00:00:31 1970 +0000
+  ! .  summary:     (31) expand
+  ! .
+  o !    changeset:   30:6e11cd4b648f
+  !\ \   parent:      28:44ecd0b9ae99
+  ! ~ !  parent:      29:cd9bb2be7593
+  !   .  user:        test
+  !   .  date:        Thu Jan 01 00:00:30 1970 +0000
+  !   .  summary:     (30) expand
+  !  /
+  o !    changeset:   28:44ecd0b9ae99
+  !\ \   parent:      1:6db2ef61d156
+  ! ~ !  parent:      26:7f25b6c2f0b9
+  !   .  user:        test
+  !   .  date:        Thu Jan 01 00:00:28 1970 +0000
+  !   .  summary:     (28) merge zero known
+  !  /
+  o !    changeset:   26:7f25b6c2f0b9
+  !\ \   parent:      18:1aa84d96232a
+  ! ! !  parent:      25:91da8ed57247
+  ! ! .  user:        test
+  ! ! .  date:        Thu Jan 01 00:00:26 1970 +0000
+  ! ! .  summary:     (26) merge one known; far right
+  ! ! .
+  ! o !  changeset:   25:91da8ed57247
+  ! !\!  parent:      21:d42a756af44d
+  ! ! !  parent:      24:a9c19a3d96b7
+  ! ! .  user:        test
+  ! ! .  date:        Thu Jan 01 00:00:25 1970 +0000
+  ! ! .  summary:     (25) merge one known; far left
+  ! ! .
+  ! o !    changeset:   24:a9c19a3d96b7
+  ! !\ \   parent:      0:e6eb3150255d
+  ! ! ~ !  parent:      23:a01cddf0766d
+  ! !   .  user:        test
+  ! !   .  date:        Thu Jan 01 00:00:24 1970 +0000
+  ! !   .  summary:     (24) merge one known; immediate right
+  ! !  /
+  ! o !    changeset:   23:a01cddf0766d
+  ! !\ \   parent:      1:6db2ef61d156
+  ! ! ~ !  parent:      22:e0d9cccacb5d
+  ! !   .  user:        test
+  ! !   .  date:        Thu Jan 01 00:00:23 1970 +0000
+  ! !   .  summary:     (23) merge one known; immediate left
+  ! !  /
+  ! o !  changeset:   22:e0d9cccacb5d
+  !/!/   parent:      18:1aa84d96232a
+  ! !    parent:      21:d42a756af44d
+  ! .    user:        test
+  ! .    date:        Thu Jan 01 00:00:22 1970 +0000
+  ! .    summary:     (22) merge two known; one far left, one far right
+  ! .
+  ! o    changeset:   21:d42a756af44d
+  ! !\   parent:      19:31ddc2c1573b
+  ! ! !  parent:      20:d30ed6450e32
+  ! ! !  user:        test
+  ! ! !  date:        Thu Jan 01 00:00:21 1970 +0000
+  ! ! !  summary:     (21) expand
+  ! ! !
+  +---o  changeset:   20:d30ed6450e32
+  ! ! |  parent:      0:e6eb3150255d
+  ! ! ~  parent:      18:1aa84d96232a
+  ! !    user:        test
+  ! !    date:        Thu Jan 01 00:00:20 1970 +0000
+  ! !    summary:     (20) merge two known; two far right
+  ! !
+  ! o    changeset:   19:31ddc2c1573b
+  ! |\   parent:      15:1dda3f72782d
+  ! ~ ~  parent:      17:44765d7c06e0
+  !      user:        test
+  !      date:        Thu Jan 01 00:00:19 1970 +0000
+  !      summary:     (19) expand
+  !
+  o    changeset:   18:1aa84d96232a
+  |\   parent:      1:6db2ef61d156
+  ~ ~  parent:      15:1dda3f72782d
+       user:        test
+       date:        Thu Jan 01 00:00:18 1970 +0000
+       summary:     (18) merge two known; two far left
+  
   $ cd ..
 
 Change graph shorten, test better with graphstyle.missing not none