# How detect_changes Maps Git Diffs to Affected Symbols and Risks

> Learn how detect_changes maps Git diffs to symbols and risks. This function parses diff hunks, queries a line-to-symbol index, and creates a structured ChangeSet for downstream risk analysis.

- Repository: [Martin Vogel/codebase-memory-mcp](https://github.com/DeusData/codebase-memory-mcp)
- Tags: how-to-guide
- Published: 2026-07-17

---

**The `detect_changes` function in DeusData/codebase-memory-mcp converts raw git diff output into a precise inventory of modified symbols by parsing unified diff hunks with zero context, querying a pre-computed line-to-symbol index, and emitting a structured ChangeSet for downstream risk analysis.**

The `detect_changes` component serves as the critical bridge between version control and static analysis in the **DeusData/codebase-memory-mcp** repository. It consumes the output of `git diff --unified=0` and identifies exactly which functions, methods, structs, and variables have been modified, enabling targeted re-analysis rather than full codebase scans.

## The Three-Stage Detection Pipeline

The implementation follows a tightly-coupled three-stage pipeline orchestrated by [`src/detectchanges.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/detectchanges.c). Each stage transforms the data from raw text to structured symbols ready for risk assessment.

### Stage 1: Parsing the Diff with Zero Context

The process begins in [`src/diff/parser.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/diff/parser.c) where `parse_git_diff()` ingests the raw git output. The parser specifically expects the output of `git diff --unified=0`, which produces hunks with zero context lines to ensure line numbers map exactly to edited lines without false positives.

The parser strips the `diff --git a/... b/...` headers and isolates the `@@ -oldStart,oldLen +newStart,newLen @@` hunk markers. It builds a map of `file → [{oldLine, newLine, length}]` describing the exact changed ranges, storing these in `DiffHunk` structures. The function discards context lines (starting with spaces) and retains only added (`+`) and removed (`-`) lines while preserving original line numbers for accurate mapping.

### Stage 2: Locating Symbols via AST Traversal

With hunks prepared, `detect_changes` iterates over each file and invokes `find_symbols_in_range(file, start, end)` from [`src/symbols/matcher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/symbols/matcher.c). This stage leverages a **line-to-symbol index** built during the initial repository scan (see [`src/symbols/indexer.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/symbols/indexer.c)) and cached in the `.cache` directory.

The matcher walks the Abstract Syntax Tree (AST) for the affected file and tests each node against the diff range using the condition `node.start ≤ diffLine ≤ node.end`. It selects the deepest node satisfying this condition, ensuring that a change inside a nested function reports that specific function rather than just the containing class or file. This depth resolution guarantees precise symbol identification for accurate risk calculation.

### Stage 3: Constructing the ChangeSet

For each located symbol, the system builds a `SymbolChange` entry containing the fully-qualified name, symbol kind (function, type, variable, etc.), file path, and hunk location. These entries aggregate into a `ChangeSet` structure defined in [`detectchanges.h`](https://github.com/DeusData/codebase-memory-mcp/blob/main/detectchanges.h).

The `serialize_changeset()` function in [`src/detectchanges.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/detectchanges.c) converts this structure to JSON, decoupling the C implementation from downstream consumers. This serialization enables the incremental analysis engine and UI components to process the changes without binding to internal data structures.

## Integration with Analysis and UI Systems

The `ChangeSet` produced by `detect_changes` feeds two major subsystems:

- **Incremental Analysis**: The [`src/analysis/incremental.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/analysis/incremental.c) module consumes the change set to re-run static analysis only on affected symbols, reducing computational overhead from O(#symbols) to O(#changed-hunks).
- **Real-time UI**: The WebSocket endpoint in [`src/server/ws.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/server/ws.c) streams the JSON-serialized changes to the frontend, enabling real-time visualization of code risks as developers modify files.

## Implementation Example

The following C code demonstrates invoking the detection pipeline from a command-line interface:

```c
// Example: invoking detectChanges from the CLI
int main(int argc, char **argv) {
    const char *diff = argv[1];          // path to a git diff file
    ChangeSet *cs = detect_changes(diff); // <-- core function
    char *json = serialize_changeset(cs);
    puts(json);
    free(json);
    free_changeset(cs);
}

```

For Go-based integrations, the library exposes bindings via cgo:

```go
// Example: Go wrapper that calls the C library via cgo
import "C"

func DetectChanges(diffPath string) ([]SymbolChange, error) {
    cDiff := C.CString(diffPath)
    defer C.free(unsafe.Pointer(cDiff))
    cs := C.detect_changes(cDiff)
    if cs == nil {
        return nil, fmt.Errorf("detectChanges failed")
    }
    // Convert the C ChangeSet to Go structs (omitted for brevity)
    return nil, nil
}

```

## Summary

- **`detect_changes`** orchestrates the pipeline in [`src/detectchanges.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/detectchanges.c), bridging git diffs and symbol analysis.
- **Zero-context parsing** (`--unified=0`) in [`src/diff/parser.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/diff/parser.c) ensures line-accurate mapping without false positives from context lines.
- **AST depth resolution** in [`src/symbols/matcher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/symbols/matcher.c) identifies the most specific symbol (e.g., nested functions) affected by each change.
- **Pre-computed indexing** via [`src/symbols/indexer.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/symbols/indexer.c) enables O(#changed-hunks) performance by caching the line-to-symbol mapping.
- **JSON serialization** decouples the detection engine from incremental analysis ([`src/analysis/incremental.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/analysis/incremental.c)) and UI updates ([`src/server/ws.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/server/ws.c)).

## Frequently Asked Questions

### Why does detect_changes use --unified=0 instead of standard diffs?

The `--unified=0` flag produces hunks containing only the changed lines with zero context. This eliminates context lines that would otherwise complicate line-number mapping, ensuring that the `oldStart` and `newStart` values in hunk headers correspond exactly to modified code. This precision prevents false positives where unchanged context lines might be mistaken for affected symbols.

### How does the system handle nested functions or methods?

The `find_symbols_in_range()` function in [`src/symbols/matcher.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/symbols/matcher.c) implements **depth resolution** by walking the AST and selecting the deepest node whose line range intersects the diff hunk. When a change occurs inside a nested function, the system identifies that specific function rather than reporting only the parent class or file, enabling granular risk assessment.

### What is the line-to-symbol index and how is it maintained?

The line-to-symbol index is a persistent mapping built by [`src/symbols/indexer.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/symbols/indexer.c) during the initial codebase scan and stored in the `.cache` directory. It maps file paths and line numbers to their corresponding AST nodes (functions, variables, types). This cache is reused across `detect_changes` invocations, making symbol lookup O(1) per line rather than requiring full AST reconstruction on every diff.

### How does the ChangeSet integrate with risk analysis?

The `ChangeSet` structure feeds [`src/analysis/incremental.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/analysis/incremental.c), which selectively re-runs static analysis rules only on modified symbols. This incremental approach identifies which changes introduce new risks (e.g., security vulnerabilities, breaking changes) without re-analyzing the entire codebase. The results are then streamed via [`src/server/ws.c`](https://github.com/DeusData/codebase-memory-mcp/blob/main/src/server/ws.c) to risk dashboards in real-time.