How Graph Diffing Compares Snapshots Over Time in Code-Review-Graph

Graph diffing in code-review-graph captures the state of a SQLite-backed knowledge graph as JSON snapshots and compares them using deterministic set operations to identify added or removed nodes, edges, and community reassignments.

The code-review-graph library stores your entire codebase as a structured knowledge graph using SQLite. To track how this graph evolves across commits or releases, the library provides a specialized graph diffing system that lets you take point-in-time snapshots and precisely compare them to reveal structural changes between any two states.

How Graph Diffing Works in Code-Review-Graph

The graph diffing implementation resides in code_review_graph/graph_diff.py and operates through four distinct phases: capturing the current state, serializing it to disk, reloading it later, and performing set-based comparisons.

Taking a Snapshot of Graph State

The take_snapshot(store) function extracts the complete state from a GraphStore instance. According to the source code in graph_diff.py (lines 15-43), it collects:

  • Global statistics via store.get_stats() for total node and edge counts
  • All nodes via store.get_all_nodes(exclude_files=False) with their qualified names, kinds, file paths, and community assignments
  • Community mappings via store.get_all_community_ids()

The function returns a dictionary containing node_count, edge_count, a nodes mapping (qualified name to metadata), and an edges set containing strings in the format source→target:kind.

Persisting Snapshots to Disk

The save_snapshot(snapshot, path) function (lines 46-53 in graph_diff.py) serializes the snapshot dictionary to JSON. To ensure deterministic output, edge sets are converted to sorted lists before dumping, making file comparisons and version control diffs predictable.

Loading Saved Snapshots

The load_snapshot(path) function (lines 56-61) reverses the process by reading the JSON file and converting edge lists back into Python sets for efficient comparison operations.

Computing Diffs Between Snapshots

The diff_snapshots(before, after) function (lines 64-122) performs the actual comparison using set arithmetic:

  • Node comparison: Converts the nodes mapping keys from both snapshots into sets (before_nodes and after_nodes), then calculates new_nodes = after_nodes - before_nodes and removed_nodes = before_nodes - after_nodes
  • Edge comparison: Performs analogous set differences on the before_edges and after_edges sets
  • Community tracking: For nodes present in both snapshots, compares community IDs and records any changes with the node name, previous ID, and new ID

The result dictionary contains:

  • new_nodes and removed_nodes (limited to 100 entries each)
  • new_edges and removed_edges (limited to 100 entries each)
  • community_changes (limited to 50 nodes)
  • summary with counters for nodes_added, nodes_removed, edges_added, edges_removed, community_moves, and total node counts before/after

Practical Example: Tracking Code Evolution

Here is a complete workflow for using graph diffing to compare snapshots over time:

from pathlib import Path
from code_review_graph.graph import GraphStore
from code_review_graph.graph_diff import take_snapshot, save_snapshot, load_snapshot, diff_snapshots

# 1️⃣ Open a graph (assumes a built graph at .code-review-graph/graph.db)

store = GraphStore(Path(".code-review-graph/graph.db"))

# 2️⃣ Take a snapshot of the current state

snap_before = take_snapshot(store)

# 3️⃣ Persist the snapshot – useful for later comparison or CI storage

save_snapshot(snap_before, Path("snap_before.json"))

# … make code changes, rebuild the graph, or run another analysis …

# 4️⃣ Take a second snapshot after the changes

snap_after = take_snapshot(store)
save_snapshot(snap_after, Path("snap_after.json"))

# 5️⃣ Load snapshots (if they were stored on disk)

snap_before = load_snapshot(Path("snap_before.json"))
snap_after  = load_snapshot(Path("snap_after.json"))

# 6️⃣ Compute the diff

diff = diff_snapshots(snap_before, snap_after)

print("Added nodes:", diff["new_nodes"])
print("Removed edges:", diff["removed_edges"])
print("Community moves:", diff["community_changes"])
print("Summary:", diff["summary"])

Summary

  • Graph diffing in code-review-graph relies on deterministic JSON snapshots captured via take_snapshot() and compared via diff_snapshots().
  • The algorithm uses simple set operations (difference) on nodes and edges, making it fast and reliable even for large codebases.
  • Snapshots capture global statistics, node metadata (including community assignments), and edge signatures in the format source→target:kind.
  • The diff_snapshots() function provides capped results (100 nodes/edges, 50 community changes) to prevent output flooding while preserving summary counts.
  • All persistence logic in code_review_graph/graph_diff.py ensures deterministic output by sorting edge lists before JSON serialization.

Frequently Asked Questions

What file format does code-review-graph use for snapshots?

Snapshots are stored as standard JSON files. The format includes sorted lists for edges to ensure deterministic output, making them suitable for version control and CI/CD pipelines where consistent file hashes matter.

How does the diff algorithm handle community detection changes?

For every node present in both snapshots, diff_snapshots() compares community IDs. If a node moved from one community to another, it records the change with the qualified name, previous community ID, and new community ID in the community_changes list (capped at 50 entries).

What are the limits on reported changes in a diff?

The diff_snapshots() function limits detailed reporting to 100 new nodes, 100 removed nodes, 100 new edges, 100 removed edges, and 50 community changes. However, the summary dictionary always reports exact total counts for all categories regardless of these limits.

Can I use graph diffing without the GraphStore database?

Yes. Once you have saved snapshots to JSON files using save_snapshot(), you can load them later with load_snapshot() and compare them using diff_snapshots() without needing the original SQLite database or GraphStore instance available.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →