How detect_changes Maps Git Diff to Affected Symbols with Risk Classification

The detect_changes MCP tool aggregates committed, unstaged, and untracked file changes via shell git commands, normalizes porcelain status output to extract clean paths, and queries the internal code graph to identify impacted symbols, while actual risk classification (CRITICAL/HIGH/MEDIUM/LOW) is computed downstream by BFS-based tools like trace_path using the cbm_hop_to_risk helper.

The detect_changes tool in the DeusData/codebase-memory-mcp repository serves as the bridge between version control state and semantic code analysis. It transforms raw git diff output into a structured JSON payload that identifies exactly which functions, methods, and classes are affected by recent changes. Understanding this mapping pipeline—and the separation between change detection and risk calculation—is essential for integrating precise impact analysis into CI/CD workflows.

Collecting the Diff from Three Git Streams

The tool constructs a comprehensive shell command in src/mcp/mcp.c (lines 5670–5776) that executes three distinct git operations to capture the complete working state. This ensures no changes are missed regardless of whether they are committed, staged, or completely untracked.

snprintf(cmd, sizeof(cmd),
#if defined(_WIN32)
    "git -C \"%s\" diff --name-only \"%s\"...HEAD 2>NUL & "
    "git -C \"%s\" diff --name-only 2>NUL & "
    "git --no-optional-locks -C \"%s\" status --porcelain "
    "--untracked-files=normal 2>NUL",
#else
    "{ git -C '%s' diff --name-only '%s'...HEAD 2>/dev/null; "
    "git -C '%s' diff --name-only 2>/dev/null; "
    "git --no-optional-locks -C '%s' status --porcelain "
    "--untracked-files=normal 2>/dev/null; } | sort -u",
#endif
    root_path, base_branch, root_path, root_path);

The three streams capture:

  1. Committed changes – git diff <base_branch>...HEAD compares the current HEAD against the specified base branch
  2. Unstaged modifications – git diff --name-only captures tracked files with uncommitted changes
  3. Untracked and newly staged files – git status --porcelain --untracked-files=normal identifies added files and new blobs

On POSIX systems, the output is piped through sort -u to deduplicate entries before processing.

Normalizing Paths and Status Codes

Raw git output requires parsing to extract usable file paths. The porcelain status format prefixes lines with two-character status codes (e.g., ?? for untracked, A for added, M for modified) and uses arrow notation for renames (old -> new).

The normalization logic in src/mcp/mcp.c (lines 1414–1428) handles this by checking for valid status prefixes and stripping them:

if (len > PAIR_LEN && line[PAIR_LEN] == ' '
    && strchr(" MADRCU?!", line[0]) && strchr(" MADRCU?!", line[1])) {
    path_line = line + PAIR_LEN + SKIP_ONE;
    char *arrow = strstr(path_line, " -> ");
    if (arrow) {
        path_line = arrow + ARROW_LEN;
    }
}

This processing ensures that renamed files resolve to their destination path and that only clean file paths enter the changed_files array.

Mapping Files to Affected Symbols

Once file paths are isolated, detect_changes queries the code graph store to identify which semantic symbols reside in those files. The function detect_add_impacted_symbols in src/mcp/mcp.c (lines 76–90) iterates through each changed file and retrieves associated nodes using cbm_store_find_nodes_by_file.

static void detect_add_impacted_symbols(cbm_store_t *store,
                                        const char *project,
                                        const char *file,
                                        yyjson_mut_doc *doc,
                                        yyjson_mut_val *impacted) {
    cbm_node_t *nodes = NULL;
    int ncount = 0;
    cbm_store_find_nodes_by_file(store, project, file, &nodes, &ncount);
    for (int i = 0; i < ncount; i++) {
        if (nodes[i].label && strcmp(nodes[i].label, "File") != 0 &&
            strcmp(nodes[i].label, "Folder") != 0 && strcmp(nodes[i].label, "Project") != 0) {
            yyjson_mut_val *item = yyjson_mut_obj(doc);
            yyjson_mut_obj_add_strcpy(doc, item, "name", nodes[i].name ? nodes[i].name : "");
            yyjson_mut_obj_add_strcpy(doc, item, "label", nodes[i].label);
            yyjson_mut_obj_add_strcpy(doc, item, "file", file);
            yyjson_mut_arr_add_val(impacted, item);
        }
    }
    cbm_store_free_nodes(nodes, ncount);
}

The filter explicitly excludes container nodes labeled File, Folder, or Project, returning only semantic symbols such as functions, methods, classes, and routes. Each symbol is serialized into the impacted_symbols array with its name, type label, and originating file path.

Understanding Risk Classification

It is critical to note that detect_changes itself does not compute risk labels. The tool provides the foundation for risk analysis by identifying the starting nodes (changed symbols) and accepting a depth parameter, but the actual risk classification occurs in downstream BFS-based tools.

The Role of BFS Depth in Risk Calculation

The depth field in the JSON output specifies how many hops to traverse through the call graph when analyzing impact. This value is consumed by tools like trace_path when performing breadth-first search outward from the changed symbols.

Translating Hop Distance to Risk Levels

Risk classification follows the principle that direct changes are highest risk, while transitive dependencies decrease in risk with distance. The helper function cbm_hop_to_risk in src/store/store.c (line 3226) translates graph distance into categorical risk levels:

  • CRITICAL – Hop 0 (the changed symbol itself)
  • HIGH – Immediate callers/callees
  • MEDIUM – Secondary dependencies
  • LOW – Distant transitive relationships

To obtain risk-annotated results, you combine detect_changes (to identify starting symbols) with trace_path(risk_labels=true) (to traverse the graph and apply the cbm_hop_to_risk mapping).

Practical Usage Examples

Basic CLI Detection

Invoke the tool from the command line to map current uncommitted changes against the main branch:

codebase-memory-mcp cli detect_changes '{"project":"my-repo","base_branch":"main"}'

The JSON output includes the changed file count and impacted symbols:

{
  "changed_files": ["src/main.c", "src/util.c"],
  "changed_count": 2,
  "impacted_symbols": [
    {"name":"main","label":"Function","file":"src/main.c"},
    {"name":"util_init","label":"Function","file":"src/util.c"}
  ],
  "depth": 5
}

Generating Risk-Annotated Impact Reports

First capture the changed symbols, then iterate through them to generate risk classifications:


# Extract changed symbol names

SYMBOLS=$(codebase-memory-mcp cli detect_changes '{"project":"my-repo"}' | jq -r '.impacted_symbols[].name')

# Run trace_path with risk classification for each symbol

for sym in $SYMBOLS; do
    codebase-memory-mcp cli trace_path \
        "{\"project\":\"my-repo\",\"function_name\":\"$sym\",\"risk_labels\":true,\"depth\":5}"
done

The trace_path response includes a risk field derived from the hop distance:

{
  "name":"process_data",
  "label":"Function",
  "risk":"HIGH"
}

JSON-RPC Programmatic Access

For integration with build systems or IDEs, call the method via JSON-RPC:

POST /rpc HTTP/1.1
Content-Type: application/json

{
  "jsonrpc":"2.0",
  "method":"detect_changes",
  "params":{
    "project":"my-repo",
    "base_branch":"main",
    "scope":"symbols"
  },
  "id":1
}

The server returns the same structured payload, allowing automated parsing of affected symbols before passing them to downstream risk analysis tools.

Summary

  • detect_changes aggregates three git streams (committed vs base, unstaged tracked, and untracked/staged) to capture the complete working tree state
  • Path normalization strips porcelain status codes and resolves rename arrows to clean file paths in src/mcp/mcp.c
  • Symbol mapping queries cbm_store_find_nodes_by_file to identify semantic entities (functions, methods, classes) while filtering out File/Folder/Project container nodes
  • Risk classification is decoupled from change detection; detect_changes provides the depth parameter and symbol list, while trace_path uses cbm_hop_to_risk in src/store/store.c to translate BFS hop counts into CRITICAL/HIGH/MEDIUM/LOW labels
  • The tool outputs yyjson-structured JSON containing changed files, impacted symbols, and traversal depth for downstream processing

Frequently Asked Questions

Does detect_changes calculate risk levels itself?

No, detect_changes only identifies which symbols reside in changed files and accepts a depth parameter. Risk classification is performed by downstream tools like trace_path when called with risk_labels=true, which uses the cbm_hop_to_risk helper function to translate graph distance into CRITICAL, HIGH, MEDIUM, or LOW labels.

What symbol types are excluded from the impacted_symbols list?

The tool explicitly filters out container nodes labeled File, Folder, and Project. Only semantic symbols such as functions, methods, classes, and routes are included in the impacted_symbols array returned in the JSON payload.

How does the tool handle renamed files in git?

When parsing git status --porcelain output, the code detects rename arrows (->) in the path string. If present, it extracts and keeps only the destination path (the new filename), ensuring that the symbol lookup references the correct current file location rather than the obsolete preimage.

What git references can I use for the base_branch parameter?

The base_branch parameter accepts any valid git reference that the three-dot diff syntax supports, including branch names (e.g., main, develop), tags (e.g., v1.0), or commit SHAs. The tool passes this reference directly to git diff <base>...HEAD to determine the set of changed files.

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 →