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):
- 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.
- 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.
Three bugs found while running
gitplot --monitor --mode verboseon 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 andwaiting for the next change.
Root cause:
GitPython'sindex.diff(None)causes git to refresh the stat cachein
.git/index, writing the file and triggering the watchdog. The next render triggersthe same thing, creating an infinite loop. The settle window in
wait()drains eventsthat 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) todiscard 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 andindividual 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 intonode
untrackedat portscratch.txt, so edges go to an auto-createduntrackedphantom 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):
GraphBuilder._colors_forhighlights nodes that ARE inhighlight_ids(old nodes) instead of nodes that are NOT inhighlight_ids(newnodes). The condition is inverted.
--mode verbose; other modes always passNoneas
highlight_ids, so new nodes are never highlighted in normal or branch mode.Fix:
_colors_forto highlight whenhighlight_ids is not None and node_id not in highlight_ids(i.e., the node is absent from the previous render → it is new).highlight_idsasOptional[frozenset[str]]soNonemeans "no highlighting"and a frozenset means "highlight nodes absent from this set."
mon.prev_node_ids(notNone) for all modes in the monitor loop.Additional: non-ASCII characters in source code
cli.pyline 194 contains an em dash and ellipsis (—,…) in a log string.builder.pyline 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.