# How Graph Diffs Reveal Git History Co‑Change Relationships in Code Review Graph

> Discover how graph diffs uncover Git history co-change relationships. See how symbols, edges, and memberships change between commits for deeper code review insights.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: deep-dive
- Published: 2026-08-16

---

**Graph diffs provide the structural, language‑level representation of Git co‑change events by capturing exactly which symbols, edges, and community memberships change between commits.**

The *code‑review‑graph* project by tirth8205 builds a full‑text, language‑aware code graph that turns Git history into queryable structural data. Understanding the relationship between **graph diffs** and **Git history co‑change** unlocks precise, program‑level insight into how code evolves together over time.

## What Graph Diffs Capture

In [`code_review_graph/graph_diff.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph_diff.py), the `take_snapshot()` function records the complete state of the code graph at any commit. A snapshot includes node counts, edge sets, and community assignments for all symbols in the repository.

The `diff_snapshots()` function then compares two snapshots to produce a structured report of changes:

- **New nodes** – symbols (functions, classes, files) added between commits
- **Removed nodes** – symbols deleted
- **New edges** – call relationships, imports, or file‑level dependencies introduced
- **Removed edges** – relationships broken
- **Community changes** – symbols that moved between logical groups

```python
from code_review_graph.graph_diff import take_snapshot, diff_snapshots

# Capture state at commit A

before = take_snapshot(store)          # graph_diff.py:L15‑L23

# Capture state at commit B

after = take_snapshot(store)

# Compute structural changes

diff = diff_snapshots(before, after)   # graph_diff.py:L64‑L78

```

## How Git History Co‑Change Is Defined

The evaluation benchmark in [`code_review_graph/eval/benchmarks/impact_accuracy.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/eval/benchmarks/impact_accuracy.py) defines **co‑change** as a specific measurement mode:

```python
MODE_CO_CHANGE = "co-change (same commit, seed excluded)"   # impact_accuracy.py:L30

```

This mode builds ground‑truth by examining Git logs: two symbols co‑change if they appear in the same commit (excluding the seed file that triggered analysis). The benchmark then validates whether the graph correctly predicts these relationships.

## The Direct Relationship: Graph Diffs as Structural Co‑Change

**Git co‑change events manifest as specific patterns in graph diffs.** When a commit modifies multiple files, the resulting diff exposes the concrete structural impact:

| Git event | Graph diff manifestation |
|-----------|--------------------------|
| Two files touched in same commit | New edges linking nodes across file boundaries |
| Function added and called | New node + new edge in single diff |
| Refactoring moves logic between modules | Community changes + edge rewire |

The benchmark treats these diff patterns as **predicted co‑change signals**. Where Git history says "these files changed together," the graph diff explains *how*: which specific symbols gained dependencies, which communities restructured, and which new call paths formed.

## Practical Workflow for Extracting Co‑Change Data

Follow this pattern to surface co‑change relationships using the library:

```python
from pathlib import Path
from code_review_graph.graph import GraphStore
from code_review_graph.graph_diff import take_snapshot, diff_snapshots

# Initialize indexed repository

store = GraphStore(root=Path("/path/to/repo"))

# 1. Snapshot before target commit

snap_before = take_snapshot(store)

# ... apply Git commit or checkout ...

# 2. Snapshot after commit

snap_after = take_snapshot(store)

# 3. Compute structural diff

diff = diff_snapshots(snap_before, snap_after)

# 4. Extract co‑change indicators

new_nodes = diff["new_nodes"]              # New symbols introduced

new_edges = diff["new_edges"]              # New dependencies formed

community_moves = diff["community_changes"] # Logical group migrations

print("New symbols:", [n["qualified_name"] for n in new_nodes])
print("New dependencies:", new_edges)
print("Community moves:", community_moves)

```

Each element in `new_edges` represents a concrete co‑change: two symbols now connected that were not before, captured because they appeared in the same commit.

## Why Graph Diffs Surpass File‑Level Co‑Change

**Precision at symbol level.** Simple file‑level co‑change flags that two files changed together but misses *which* functions now interact. The `diff_snapshots` output in [`graph_diff.py`](https://github.com/tirth8205/code-review-graph/blob/main/graph_diff.py) exposes exact qualified names and edge types.

**Community dynamics captured.** When `diff_snapshots` reports `community_changes` (lines 84‑99), it records symbols that shifted logical groups. This structural refactoring signal is invisible to raw Git logs.

**Dependency directionality.** New edges carry semantic meaning—imports, calls, containment—that raw co‑change counts cannot express.

## Key Source Files and Their Roles

| File | Purpose | Co‑change relevance |
|------|---------|---------------------|
| [`code_review_graph/graph_diff.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph_diff.py) | `take_snapshot()` and `diff_snapshots()` implementation | Produces the structural diff that predicts co‑change |
| [`code_review_graph/eval/benchmarks/impact_accuracy.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/eval/benchmarks/impact_accuracy.py) | Defines `MODE_CO_CHANGE` and ground‑truth construction | Validates that graph diffs correctly identify Git co‑change |
| [`code_review_graph/graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph.py) | `GraphStore` with `get_stats()` and community IDs | Provides node/edge data used in snapshots |
| [`tests/test_eval.py`](https://github.com/tirth8205/code-review-graph/blob/main/tests/test_eval.py) | Test suite for evaluation modes | Confirms co‑change handling behavior |

## Summary

- **Graph diffs are the structural realization of Git co‑change events**—what Git tracks at file level, the code graph exposes at symbol and edge level
- The `diff_snapshots` function in [`code_review_graph/graph_diff.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph_diff.py) generates predictions that the `impact_accuracy` benchmark validates against Git history
- New edges in diffs directly correspond to co‑changing symbols introduced in the same commit
- Community changes (line 84‑99) capture refactoring co‑change invisible to file‑level analysis

## Frequently Asked Questions

### How does `diff_snapshots` identify which symbols changed together?

`diff_snapshots` compares node and edge sets between two snapshots taken via `take_snapshot`. Any edge connecting nodes from different files that appears in the diff indicates those symbols were introduced or modified in the same commit—this is the structural definition of co‑change used by the benchmark.

### Can graph diffs detect co‑change without rebuilding the entire graph?

No. The current implementation requires full snapshots before and after because `take_snapshot()` calls `store.get_stats()` to capture complete node, edge, and community state. Incremental diffing would require tracking per‑commit delta events during indexing.

### What is the difference between `MODE_CO_CHANGE` and other impact accuracy modes?

`MODE_CO_CHANGE` specifically tests whether symbols appearing in the same Git commit (excluding the seed) are connected in the graph. Other modes in [`impact_accuracy.py`](https://github.com/tirth8205/code-review-graph/blob/main/impact_accuracy.py) test different structural predictions such as direct callers or community neighbors without the Git‑history constraint.

### When would community changes indicate co‑change versus independent refactoring?

A community change in `diff_snapshots` signals co‑change when it occurs alongside new edges linking the moved symbol to other modified symbols. Isolated community migration without edge changes suggests refactoring that did not co‑evolve with other logic—still valuable, but a different evolution pattern.