[PATCH 2 of 5] graphlog: split the actual DAG grapher out into a separate method

Peter Arrenbrecht peter.arrenbrecht at gmail.com
Thu Nov 6 06:40:23 CST 2008


# HG changeset patch
# User Peter Arrenbrecht <peter.arrenbrecht at gmail.com>
# Date 1225894450 -3600
graphlog: split the actual DAG grapher out into a separate method

This allows extensions like pbranch to use asciigraph() to graph dependencies
for patch branches, but could be used for basically any DAG.

diff --git a/hgext/graphlog.py b/hgext/graphlog.py
--- a/hgext/graphlog.py
+++ b/hgext/graphlog.py
@@ -198,38 +198,26 @@
     else:
         return (len(repo) - 1, 0)
 
-def graphlog(ui, repo, path=None, **opts):
-    """show revision history alongside an ASCII revision graph
+def asciigraph(ui, grapher):
+    """prints an ASCII graph of the DAG returned by the grapher
 
-    Print a revision history alongside a revision graph drawn with
-    ASCII characters.
+    grapher is a generator that emits tuples with the following elements:
 
-    Nodes printed as an @ character are parents of the working
-    directory.
+      - Character to use as node's symbol.
+      - List of lines to display as the node's text.
+      - Column of the current node in the set of ongoing edges.
+      - Edges; a list of (col, next_col) indicating the edges between
+        the current node and its parents.
+      - Number of columns (ongoing edges) in the current revision.
+      - The difference between the number of columns (ongoing edges)
+        in the next revision and the number of columns (ongoing edges)
+        in the current revision. That is: -1 means one column removed;
+        0 means no columns added or removed; 1 means one column added.
     """
-
-    limit = get_limit(opts["limit"])
-    (start_rev, stop_rev) = get_revs(repo, opts["rev"])
-    stop_rev = max(stop_rev, start_rev - limit + 1)
-    if start_rev == nullrev:
-        return
-    cs_printer = show_changeset(ui, repo, opts)
-    if path:
-        path = canonpath(repo.root, os.getcwd(), path)
-    if path:
-        grapher = filelog_grapher(repo, path, start_rev, stop_rev)
-    else:
-        grapher = revision_grapher(repo, start_rev, stop_rev)
-    repo_parents = repo.dirstate.parents()
     prev_n_columns_diff = 0
     prev_node_index = 0
-
-    for (rev, node, node_index, edges, n_columns, n_columns_diff) in grapher:
-        # log_strings is the list of all log strings to draw alongside
-        # the graph.
-        ui.pushbuffer()
-        cs_printer.show(rev, node)
-        log_strings = ui.popbuffer().split("\n")[:-1]
+    for (node_ch, node_lines, node_index, edges, n_columns, n_columns_diff) in grapher:
+        # node_lines is the list of all text lines to draw alongside the graph
 
         if n_columns_diff == -1:
             # Transform
@@ -247,7 +235,7 @@
         #     |  / /         |   | |  # <--- padding line
         #     o | |          |  / /
         #                    o | |
-        add_padding_line = (len(log_strings) > 2 and
+        add_padding_line = (len(node_lines) > 2 and
                             n_columns_diff == -1 and
                             [x for (x, y) in edges if x + 1 < y])
 
@@ -258,14 +246,10 @@
         #     | o | |    into  | o / /   # <--- fixed nodeline tail
         #     | |/ /           | |/ /
         #     o | |            o | |
-        fix_nodeline_tail = len(log_strings) <= 2 and not add_padding_line
+        fix_nodeline_tail = len(node_lines) <= 2 and not add_padding_line
 
-        # nodeline is the line containing the node character (@ or o).
+        # nodeline is the line containing the node character (typically o)
         nodeline = ["|", " "] * node_index
-        if node in repo_parents:
-            node_ch = "@"
-        else:
-            node_ch = "o"
         nodeline.extend([node_ch, " "])
 
         nodeline.extend(
@@ -274,7 +258,7 @@
                 prev_n_columns_diff, fix_nodeline_tail))
 
         # shift_interline is the line containing the non-vertical
-        # edges between this entry and the next.
+        # edges between this entry and the next
         shift_interline = ["|", " "] * node_index
         if n_columns_diff == -1:
             n_spaces = 1
@@ -288,33 +272,72 @@
         shift_interline.extend(n_spaces * [" "])
         shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
 
-        # Draw edges from the current node to its parents.
+        # draw edges from the current node to its parents
         draw_edges(edges, nodeline, shift_interline)
 
-        # lines is the list of all graph lines to print.
+        # lines is the list of all graph lines to print
         lines = [nodeline]
         if add_padding_line:
             lines.append(get_padding_line(node_index, n_columns, edges))
         lines.append(shift_interline)
 
-        # Make sure that there are as many graph lines as there are
-        # log strings.
-        while len(log_strings) < len(lines):
-            log_strings.append("")
-        if len(lines) < len(log_strings):
+        # make sure that there are as many graph lines as there are
+        # log strings
+        while len(node_lines) < len(lines):
+            node_lines.append("")
+        if len(lines) < len(node_lines):
             extra_interline = ["|", " "] * (n_columns + n_columns_diff)
-            while len(lines) < len(log_strings):
+            while len(lines) < len(node_lines):
                 lines.append(extra_interline)
 
-        # Print lines.
+        # print lines
         indentation_level = max(n_columns, n_columns + n_columns_diff)
-        for (line, logstr) in zip(lines, log_strings):
+        for (line, logstr) in zip(lines, node_lines):
             ui.write(format_line(line, indentation_level, logstr))
 
-        # ...and start over.
+        # ...and start over
         prev_node_index = node_index
         prev_n_columns_diff = n_columns_diff
 
+def graphlog(ui, repo, path=None, **opts):
+    """show revision history alongside an ASCII revision graph
+
+    Print a revision history alongside a revision graph drawn with
+    ASCII characters.
+
+    Nodes printed as an @ character are parents of the working
+    directory.
+    """
+
+    limit = get_limit(opts["limit"])
+    (start_rev, stop_rev) = get_revs(repo, opts["rev"])
+    stop_rev = max(stop_rev, start_rev - limit + 1)
+    if start_rev == nullrev:
+        return
+    if path:
+        path = canonpath(repo.root, os.getcwd(), path)
+    if path:
+        revgrapher = filelog_grapher(repo, path, start_rev, stop_rev)
+    else:
+        revgrapher = revision_grapher(repo, start_rev, stop_rev)
+
+    repo_parents = repo.dirstate.parents()
+    cs_printer = show_changeset(ui, repo, opts)
+    def grapher():
+        for (rev, node, node_index, edges, n_columns, n_columns_diff) in revgrapher:
+            # log_strings is the list of all log strings to draw alongside
+            # the graph.
+            ui.pushbuffer()
+            cs_printer.show(rev, node)
+            log_strings = ui.popbuffer().split("\n")[:-1]
+            if node in repo_parents:
+                node_ch = "@"
+            else:
+                node_ch = "o"
+            yield (node_ch, log_strings, node_index, edges, n_columns, n_columns_diff)
+
+    asciigraph(ui, grapher())
+
 cmdtable = {
     "glog":
         (graphlog,


More information about the Mercurial-devel mailing list