Skip to content

graflo.plot.merge3

Draw a three-way merge: where two branches met, and the lineage they met on.

Two pictures, because a merge raises two questions.

:func:plot_merge3_preview answers where in the model did they collide — the slot tree, with the contested slots carrying what each branch did and what the ancestor had. Slots are paths that contain one another, so the tree is already there in the data; drawing it puts a rename of a vertex above the field edits that live inside it, which is the relationship a flat list of conflicts loses.

:func:plot_history answers which two commits, and from where — the commit DAG, with the merge base marked. First-parent edges are solid because that is the line a commit's ops are a diff along; every other parent is dashed.

Attributes

COMMIT_SHAPE = {'merge': 'doubleoctagon', 'merge3': 'doubleoctagon', 'revert': 'octagon', 'edit': 'box'} module-attribute

SIDE_COLOR = {'left': '#B7D1DF', 'right': '#BEDFC8'} module-attribute

SLOT_COLOR = {'contested': '#C0392B', 'clean': '#7F8C8D', 'base': '#5D6D7E'} module-attribute

__all__ = ['COMMIT_SHAPE', 'SIDE_COLOR', 'SLOT_COLOR', 'build_history_graph', 'build_merge3_graph', 'plot_history', 'plot_merge3_preview'] module-attribute

Classes

Functions:

build_history_graph(history, *, heads=(), merge_base=None)

The commit DAG as a networkx graph.

Parameters:

Name Type Description Default
history Any

A :class:~graflo.architecture.evolution.history.History.

required
heads Collection[str]

Commit ids to mark as the sides being reconciled.

()
merge_base str | None

The common ancestor, marked as such.

None

Returns:

Type Description
DiGraph

A graph with one node per commit, edges pointing parent to child.

Source code in graflo/plot/merge3.py
def build_history_graph(
    history: Any,
    *,
    heads: Collection[str] = (),
    merge_base: str | None = None,
) -> nx.DiGraph:
    """The commit DAG as a networkx graph.

    Args:
        history: A :class:`~graflo.architecture.evolution.history.History`.
        heads: Commit ids to mark as the sides being reconciled.
        merge_base: The common ancestor, marked as such.

    Returns:
        A graph with one node per commit, edges pointing parent to child.
    """
    graph = nx.DiGraph()
    graph.graph["graph"] = {
        "rankdir": "LR",
        "fontname": "Helvetica",
        "labelloc": "t",
        "label": "commit history",
    }
    graph.graph["node"] = {"fontname": "Helvetica", "style": "filled"}
    graph.graph["edge"] = {"fontname": "Helvetica"}

    head_ids = set(heads)
    for commit in history.commits:
        role = (
            "base"
            if commit.id == merge_base
            else "head"
            if commit.id in head_ids
            else commit.kind
        )
        fill = {
            "base": "#FFE5B4",
            "head": "#B7D1DF",
        }.get(role, "#FFFFFF")
        label = commit.id[:8]
        if commit.label:
            label = f"{label}\\n{commit.label}"
        if commit.id == merge_base:
            label = f"{label}\\n(merge base)"
        graph.add_node(
            sanitize_id(commit.id),
            label=label,
            shape=COMMIT_SHAPE.get(commit.kind, "box"),
            fillcolor=fill,
            commit=commit.id,
            role=role,
        )
    for commit in history.commits:
        for position, parent in enumerate(commit.parents):
            graph.add_edge(
                sanitize_id(parent),
                sanitize_id(commit.id),
                # A commit's ops are a diff from its *first* parent; the other
                # parents are lineage, not derivation.
                style="solid" if position == 0 else "dashed",
                color="#2C3E50" if position == 0 else "#95A5A6",
            )
    return graph

build_merge3_graph(preview)

The slot tree as a networkx graph, styled but not drawn.

Parameters:

Name Type Description Default
preview Merge3Preview

What :func:~graflo.architecture.evolution.preview.build_merge3_preview returned.

required

Returns:

Type Description
DiGraph

A graph whose attributes are Graphviz attributes; every node carries

DiGraph

its slot path as slot.

Source code in graflo/plot/merge3.py
def build_merge3_graph(preview: Merge3Preview) -> nx.DiGraph:
    """The slot tree as a networkx graph, styled but not drawn.

    Args:
        preview: What
            :func:`~graflo.architecture.evolution.preview.build_merge3_preview`
            returned.

    Returns:
        A graph whose attributes are Graphviz attributes; every node carries
        its slot path as ``slot``.
    """
    graph = nx.DiGraph()
    verdict = "clean" if preview.clean else f"{preview.conflicts} conflict(s) to decide"
    graph.graph["graph"] = {
        "rankdir": "LR",
        "fontname": "Helvetica",
        "labelloc": "t",
        "label": f"three-way merge — {verdict}",
    }
    graph.graph["node"] = {"fontname": "Helvetica", "shape": "plain"}
    graph.graph["edge"] = {"fontname": "Helvetica", "color": "#95A5A6"}

    taken: dict[str, str] = {}
    for node in preview.nodes:
        graph.add_node(
            sanitize_id(node.id, taken),
            label=_slot_table(node),
            slot=node.id,
            contested=str(node.contested).lower(),
        )
    for node in preview.nodes:
        if node.parent is None or node.parent not in taken:
            continue
        graph.add_edge(
            taken[node.parent],
            taken[node.id],
            penwidth="1.6" if node.contested else "1.0",
            color=SLOT_COLOR["contested"] if node.contested else "#95A5A6",
        )
    return graph

plot_history(history, path, *, heads=(), merge_base=None, output_format=None, prog='dot', dpi=None)

Draw the commit DAG of history to path.

Parameters:

Name Type Description Default
history Any

A :class:~graflo.architecture.evolution.history.History.

required
path str | PathLike[str]

Where to write; the suffix picks the format.

required
heads Collection[str]

Commit ids to mark as the sides being reconciled.

()
merge_base str | None

The common ancestor, marked as such.

None
output_format str | None

Override the format the suffix implies.

None
prog str

Graphviz layout program.

'dot'
dpi int | None

Raster resolution, for png.

None

Returns:

Type Description
Path

The path written.

Source code in graflo/plot/merge3.py
def plot_history(
    history: Any,
    path: str | os.PathLike[str],
    *,
    heads: Collection[str] = (),
    merge_base: str | None = None,
    output_format: str | None = None,
    prog: str = "dot",
    dpi: int | None = None,
) -> Path:
    """Draw the commit DAG of *history* to *path*.

    Args:
        history: A :class:`~graflo.architecture.evolution.history.History`.
        path: Where to write; the suffix picks the format.
        heads: Commit ids to mark as the sides being reconciled.
        merge_base: The common ancestor, marked as such.
        output_format: Override the format the suffix implies.
        prog: Graphviz layout program.
        dpi: Raster resolution, for ``png``.

    Returns:
        The path written.
    """
    graph = build_history_graph(history, heads=heads, merge_base=merge_base)
    return draw(to_agraph(graph), path, output_format=output_format, prog=prog, dpi=dpi)

plot_merge3_preview(preview, path, *, output_format=None, prog='dot', dpi=None)

Draw the slot tree of preview to path.

Parameters:

Name Type Description Default
preview Merge3Preview

What build_merge3_preview returned.

required
path str | PathLike[str]

Where to write; the suffix picks the format (svg/pdf/png/dot).

required
output_format str | None

Override the format the suffix implies.

None
prog str

Graphviz layout program.

'dot'
dpi int | None

Raster resolution, for png.

None

Returns:

Type Description
Path

The path written.

Source code in graflo/plot/merge3.py
def plot_merge3_preview(
    preview: Merge3Preview,
    path: str | os.PathLike[str],
    *,
    output_format: str | None = None,
    prog: str = "dot",
    dpi: int | None = None,
) -> Path:
    """Draw the slot tree of *preview* to *path*.

    Args:
        preview: What ``build_merge3_preview`` returned.
        path: Where to write; the suffix picks the format (svg/pdf/png/dot).
        output_format: Override the format the suffix implies.
        prog: Graphviz layout program.
        dpi: Raster resolution, for ``png``.

    Returns:
        The path written.
    """
    graph = build_merge3_graph(preview)
    return draw(to_agraph(graph), path, output_format=output_format, prog=prog, dpi=dpi)