hgext/graphlog.py
changeset 9371 571a7acb4544
parent 9370 b360addfbe0e
child 9631 1c34fca5d785
equal deleted inserted replaced
9370:b360addfbe0e 9371:571a7acb4544
    98         c = " "
    98         c = " "
    99     line.extend([c, " "])
    99     line.extend([c, " "])
   100     line.extend(["|", " "] * (n_columns - ni - 1))
   100     line.extend(["|", " "] * (n_columns - ni - 1))
   101     return line
   101     return line
   102 
   102 
   103 def ascii(ui, dag):
   103 def ascii(ui, base, type, char, text, coldata):
   104     """prints an ASCII graph of the DAG
   104     """prints an ASCII graph of the DAG
   105 
   105 
   106     dag is a generator that emits tuples with the following elements:
   106     takes the following arguments (one call per node in the graph):
   107 
   107 
       
   108       - ui to write to
       
   109       - A list we can keep the needed state in
   108       - Column of the current node in the set of ongoing edges.
   110       - Column of the current node in the set of ongoing edges.
   109       - Type indicator of node data == ASCIIDATA.
   111       - Type indicator of node data == ASCIIDATA.
   110       - Payload: (char, lines):
   112       - Payload: (char, lines):
   111         - Character to use as node's symbol.
   113         - Character to use as node's symbol.
   112         - List of lines to display as the node's text.
   114         - List of lines to display as the node's text.
   117         in the next revision and the number of columns (ongoing edges)
   119         in the next revision and the number of columns (ongoing edges)
   118         in the current revision. That is: -1 means one column removed;
   120         in the current revision. That is: -1 means one column removed;
   119         0 means no columns added or removed; 1 means one column added.
   121         0 means no columns added or removed; 1 means one column added.
   120     """
   122     """
   121 
   123 
   122     base = [0, 0]
   124     idx, edges, ncols, coldiff = coldata
   123     for idx, type, (char, text), edges, ncols, coldiff in dag:
   125     assert -2 < coldiff < 2
   124 
   126     if coldiff == -1:
   125         assert -2 < coldiff < 2
   127         # Transform
   126         if coldiff == -1:
       
   127             # Transform
       
   128             #
       
   129             #     | | |        | | |
       
   130             #     o | |  into  o---+
       
   131             #     |X /         |/ /
       
   132             #     | |          | |
       
   133             fix_long_right_edges(edges)
       
   134 
       
   135         # add_padding_line says whether to rewrite
       
   136         #
   128         #
   137         #     | | | |        | | | |
   129         #     | | |        | | |
   138         #     | o---+  into  | o---+
   130         #     o | |  into  o---+
   139         #     |  / /         |   | |  # <--- padding line
   131         #     |X /         |/ /
   140         #     o | |          |  / /
   132         #     | |          | |
   141         #                    o | |
   133         fix_long_right_edges(edges)
   142         add_padding_line = (len(text) > 2 and
   134 
   143                             coldiff == -1 and
   135     # add_padding_line says whether to rewrite
   144                             [x for (x, y) in edges if x + 1 < y])
   136     #
   145 
   137     #     | | | |        | | | |
   146         # fix_nodeline_tail says whether to rewrite
   138     #     | o---+  into  | o---+
   147         #
   139     #     |  / /         |   | |  # <--- padding line
   148         #     | | o | |        | | o | |
   140     #     o | |          |  / /
   149         #     | | |/ /         | | |/ /
   141     #                    o | |
   150         #     | o | |    into  | o / /   # <--- fixed nodeline tail
   142     add_padding_line = (len(text) > 2 and coldiff == -1 and
   151         #     | |/ /           | |/ /
   143                         [x for (x, y) in edges if x + 1 < y])
   152         #     o | |            o | |
   144 
   153         fix_nodeline_tail = len(text) <= 2 and not add_padding_line
   145     # fix_nodeline_tail says whether to rewrite
   154 
   146     #
   155         # nodeline is the line containing the node character (typically o)
   147     #     | | o | |        | | o | |
   156         nodeline = ["|", " "] * idx
   148     #     | | |/ /         | | |/ /
   157         nodeline.extend([char, " "])
   149     #     | o | |    into  | o / /   # <--- fixed nodeline tail
   158 
   150     #     | |/ /           | |/ /
   159         nodeline.extend(
   151     #     o | |            o | |
   160             get_nodeline_edges_tail(idx, base[1], ncols, coldiff,
   152     fix_nodeline_tail = len(text) <= 2 and not add_padding_line
   161                                     base[0], fix_nodeline_tail))
   153 
   162 
   154     # nodeline is the line containing the node character (typically o)
   163         # shift_interline is the line containing the non-vertical
   155     nodeline = ["|", " "] * idx
   164         # edges between this entry and the next
   156     nodeline.extend([char, " "])
   165         shift_interline = ["|", " "] * idx
   157 
   166         if coldiff == -1:
   158     nodeline.extend(
   167             n_spaces = 1
   159         get_nodeline_edges_tail(idx, base[1], ncols, coldiff,
   168             edge_ch = "/"
   160                                 base[0], fix_nodeline_tail))
   169         elif coldiff == 0:
   161 
   170             n_spaces = 2
   162     # shift_interline is the line containing the non-vertical
   171             edge_ch = "|"
   163     # edges between this entry and the next
   172         else:
   164     shift_interline = ["|", " "] * idx
   173             n_spaces = 3
   165     if coldiff == -1:
   174             edge_ch = "\\"
   166         n_spaces = 1
   175         shift_interline.extend(n_spaces * [" "])
   167         edge_ch = "/"
   176         shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
   168     elif coldiff == 0:
   177 
   169         n_spaces = 2
   178         # draw edges from the current node to its parents
   170         edge_ch = "|"
   179         draw_edges(edges, nodeline, shift_interline)
   171     else:
   180 
   172         n_spaces = 3
   181         # lines is the list of all graph lines to print
   173         edge_ch = "\\"
   182         lines = [nodeline]
   174     shift_interline.extend(n_spaces * [" "])
   183         if add_padding_line:
   175     shift_interline.extend([edge_ch, " "] * (ncols - idx - 1))
   184             lines.append(get_padding_line(idx, ncols, edges))
   176 
   185         lines.append(shift_interline)
   177     # draw edges from the current node to its parents
   186 
   178     draw_edges(edges, nodeline, shift_interline)
   187         # make sure that there are as many graph lines as there are
   179 
   188         # log strings
   180     # lines is the list of all graph lines to print
   189         while len(text) < len(lines):
   181     lines = [nodeline]
   190             text.append("")
   182     if add_padding_line:
   191         if len(lines) < len(text):
   183         lines.append(get_padding_line(idx, ncols, edges))
   192             extra_interline = ["|", " "] * (ncols + coldiff)
   184     lines.append(shift_interline)
   193             while len(lines) < len(text):
   185 
   194                 lines.append(extra_interline)
   186     # make sure that there are as many graph lines as there are
   195 
   187     # log strings
   196         # print lines
   188     while len(text) < len(lines):
   197         indentation_level = max(ncols, ncols + coldiff)
   189         text.append("")
   198         for (line, logstr) in zip(lines, text):
   190     if len(lines) < len(text):
   199             ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
   191         extra_interline = ["|", " "] * (ncols + coldiff)
   200             ui.write(ln.rstrip() + '\n')
   192         while len(lines) < len(text):
   201 
   193             lines.append(extra_interline)
   202         # ... and start over
   194 
   203         base[0] = coldiff
   195     # print lines
   204         base[1] = idx
   196     indentation_level = max(ncols, ncols + coldiff)
       
   197     for (line, logstr) in zip(lines, text):
       
   198         ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
       
   199         ui.write(ln.rstrip() + '\n')
       
   200 
       
   201     # ... and start over
       
   202     base[0] = coldiff
       
   203     base[1] = idx
   205 
   204 
   206 def get_revs(repo, rev_opt):
   205 def get_revs(repo, rev_opt):
   207     if rev_opt:
   206     if rev_opt:
   208         revs = revrange(repo, rev_opt)
   207         revs = revrange(repo, rev_opt)
   209         return (max(revs), min(revs))
   208         return (max(revs), min(revs))
   215                "only_merges", "user", "only_branch", "prune", "newest_first",
   214                "only_merges", "user", "only_branch", "prune", "newest_first",
   216                "no_merges", "include", "exclude"]:
   215                "no_merges", "include", "exclude"]:
   217         if op in opts and opts[op]:
   216         if op in opts and opts[op]:
   218             raise util.Abort(_("--graph option is incompatible with --%s") % op)
   217             raise util.Abort(_("--graph option is incompatible with --%s") % op)
   219 
   218 
   220 def generate(dag, displayer, showparents, edgefn):
   219 def generate(ui, dag, displayer, showparents, edgefn):
   221     seen = []
   220     seen, base = [], [0, 0]
   222     for rev, type, ctx, parents in dag:
   221     for rev, type, ctx, parents in dag:
   223         char = ctx.node() in showparents and '@' or 'o'
   222         char = ctx.node() in showparents and '@' or 'o'
   224         displayer.show(ctx)
   223         displayer.show(ctx)
   225         lines = displayer.hunk.pop(rev).split('\n')[:-1]
   224         lines = displayer.hunk.pop(rev).split('\n')[:-1]
   226         cols = edgefn(seen, rev, parents)
   225         ascii(ui, base, type, char, lines, edgefn(seen, rev, parents))
   227         yield cols[0], type, (char, lines), cols[1], cols[2], cols[3]
       
   228 
   226 
   229 def graphlog(ui, repo, path=None, **opts):
   227 def graphlog(ui, repo, path=None, **opts):
   230     """show revision history alongside an ASCII revision graph
   228     """show revision history alongside an ASCII revision graph
   231 
   229 
   232     Print a revision history alongside a revision graph drawn with
   230     Print a revision history alongside a revision graph drawn with
   250     else:
   248     else:
   251         revdag = graphmod.revisions(repo, start, stop)
   249         revdag = graphmod.revisions(repo, start, stop)
   252 
   250 
   253     displayer = show_changeset(ui, repo, opts, buffered=True)
   251     displayer = show_changeset(ui, repo, opts, buffered=True)
   254     showparents = [ctx.node() for ctx in repo[None].parents()]
   252     showparents = [ctx.node() for ctx in repo[None].parents()]
   255     gen = generate(revdag, displayer, showparents, asciiedges)
   253     generate(ui, revdag, displayer, showparents, asciiedges)
   256     ascii(ui, gen)
       
   257 
   254 
   258 def graphrevs(repo, nodes, opts):
   255 def graphrevs(repo, nodes, opts):
   259     limit = cmdutil.loglimit(opts)
   256     limit = cmdutil.loglimit(opts)
   260     nodes.reverse()
   257     nodes.reverse()
   261     if limit < sys.maxint:
   258     if limit < sys.maxint:
   287 
   284 
   288     o = repo.changelog.nodesbetween(o, revs)[0]
   285     o = repo.changelog.nodesbetween(o, revs)[0]
   289     revdag = graphrevs(repo, o, opts)
   286     revdag = graphrevs(repo, o, opts)
   290     displayer = show_changeset(ui, repo, opts, buffered=True)
   287     displayer = show_changeset(ui, repo, opts, buffered=True)
   291     showparents = [ctx.node() for ctx in repo[None].parents()]
   288     showparents = [ctx.node() for ctx in repo[None].parents()]
   292     gen = generate(revdag, displayer, showparents, asciiedges)
   289     generate(ui, revdag, displayer, showparents, asciiedges)
   293     ascii(ui, gen)
       
   294 
   290 
   295 def gincoming(ui, repo, source="default", **opts):
   291 def gincoming(ui, repo, source="default", **opts):
   296     """show the incoming changesets alongside an ASCII revision graph
   292     """show the incoming changesets alongside an ASCII revision graph
   297 
   293 
   298     Print the incoming changesets alongside a revision graph drawn with
   294     Print the incoming changesets alongside a revision graph drawn with
   338 
   334 
   339         chlist = other.changelog.nodesbetween(incoming, revs)[0]
   335         chlist = other.changelog.nodesbetween(incoming, revs)[0]
   340         revdag = graphrevs(other, chlist, opts)
   336         revdag = graphrevs(other, chlist, opts)
   341         displayer = show_changeset(ui, other, opts, buffered=True)
   337         displayer = show_changeset(ui, other, opts, buffered=True)
   342         showparents = [ctx.node() for ctx in repo[None].parents()]
   338         showparents = [ctx.node() for ctx in repo[None].parents()]
   343         gen = generate(revdag, displayer, showparents, asciiedges)
   339         generate(ui, revdag, displayer, showparents, asciiedges)
   344         ascii(ui, gen)
       
   345 
   340 
   346     finally:
   341     finally:
   347         if hasattr(other, 'close'):
   342         if hasattr(other, 'close'):
   348             other.close()
   343             other.close()
   349         if cleanup:
   344         if cleanup: