Skip to content

fix: monitor loop, edge targets for index nodes, and highlight coloring #16

@rcronk

Description

@rcronk

Three bugs found while running gitplot --monitor --mode verbose on a fresh repo.

Bug 1: Monitor mode continuously re-renders instead of once per change

After any filesystem change (e.g. git init, touch db.py), the monitor logs
"Change detected - re-rendering..." in a tight loop instead of rendering once and
waiting for the next change.

Root cause: GitPython's index.diff(None) causes git to refresh the stat cache
in .git/index, writing the file and triggering the watchdog. The next render triggers
the same thing, creating an infinite loop. The settle window in wait() drains events
that arrive before the render starts, but self-induced events arrive during/after the
render and are never drained.

Fix: Add a post-render drain in Monitor (sleep briefly then clear the event) to
discard any filesystem events caused by the render itself.

Bug 2: Staged/unstaged/untracked file edges point to wrong node

In verbose mode with untracked or staged files, the edges from the container node
("Untracked", "Staged Changes", "Unstaged Changes") connect back to a phantom node
(e.g. untracked) instead of the actual file nodes. Both the edge labels and
individual file nodes appear in the diagram, but disconnected.

Root cause: The graphviz Python library's _quote_edge() method splits node IDs on
: to handle port notation. Node IDs like "untracked:scratch.txt" are split into
node untracked at port scratch.txt, so edges go to an auto-created untracked
phantom node instead of "untracked:scratch.txt".

Fix: Replace : with | as the separator in staged/unstaged/untracked node IDs
(f"untracked|{path}" etc.) since | has no special meaning in DOT edge statements.

Bug 3: All nodes turn yellow (highlight coloring is inverted and mode-restricted)

In monitor mode, after any filesystem change, all nodes become yellow instead of only
the newly added nodes.

Root causes (two issues):

  1. The highlight logic in GraphBuilder._colors_for highlights nodes that ARE in
    highlight_ids (old nodes) instead of nodes that are NOT in highlight_ids (new
    nodes). The condition is inverted.
  2. Highlighting only activates for --mode verbose; other modes always pass None
    as highlight_ids, so new nodes are never highlighted in normal or branch mode.

Fix:

  • Change _colors_for to highlight when highlight_ids is not None and node_id not in highlight_ids (i.e., the node is absent from the previous render → it is new).
  • Keep highlight_ids as Optional[frozenset[str]] so None means "no highlighting"
    and a frozenset means "highlight nodes absent from this set."
  • Pass mon.prev_node_ids (not None) for all modes in the monitor loop.

Additional: non-ASCII characters in source code

cli.py line 194 contains an em dash and ellipsis (, ) in a log string.
builder.py line 203 contains an ellipsis () used in commit message truncation;
line 287 contains a right arrow () in a graph node label.
These should all be replaced with ASCII equivalents.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions