How Egonex-AI's Incremental Analysis Detects and Manages Changed Files: A Technical Deep Dive
Egonex-AI's incremental analysis uses Git diffs to identify modified files, evaluates staleness via the isStale function, and surgically updates the knowledge graph through mergeGraphUpdate to avoid expensive full rebuilds.
The Egonex-AI/Understand-Anything repository implements a sophisticated incremental analysis pipeline that keeps code knowledge graphs synchronized with repository changes without recomputing the entire graph on every run. This system leverages Git integration, content fingerprinting, and surgical graph mutations to detect and manage changed files efficiently. Understanding how incremental analysis detects and manages changed files reveals the architecture behind performant, large-scale code comprehension systems.
Detecting File Changes with Git Integration
The detection mechanism begins with Git-based comparison to identify exactly what has evolved since the last analysis.
The getChangedFiles Helper
At the core of change detection lies the getChangedFiles utility implemented in staleness.ts. This function executes a git diff command comparing the previously analyzed commit hash stored in the knowledge graph metadata against the current HEAD. It returns a plain array of file paths that have been added, removed, or modified in the working directory.
In understand-anything-plugin/packages/core/src/staleness.ts (lines 13-26), the implementation shells out to Git to capture the delta, ensuring the system recognizes every filesystem mutation that could affect the knowledge graph's accuracy. This approach guarantees that no manual file tracking is required—the version control system serves as the single source of truth for incremental analysis.
Evaluating Staleness Status
Once changed files are identified, the pipeline must determine whether the existing knowledge graph requires rebuilding.
The isStale Wrapper Function
The isStale function (lines 31-42 in staleness.ts) wraps the diff result and provides a clean API for the analysis pipeline. It returns an object containing:
- A boolean
staleflag indicating whether any changes exist - The complete list of
changedFilesrequiring attention
The calling code uses this boolean to decide between two execution paths: reusing the persisted knowledge graph when stale is false, or triggering an incremental rebuild when true. This early-exit optimization prevents unnecessary computational overhead when repositories remain unchanged between analysis runs.
Merging Updates into the Knowledge Graph
When staleness is confirmed, the system performs surgical updates rather than regenerating the entire graph structure.
The mergeGraphUpdate Workflow
The mergeGraphUpdate function (lines 54-90 in staleness.ts) implements the critical merge logic that maintains graph integrity while incorporating new analysis data. This function operates through a precise three-step process:
- Pruning obsolete nodes – It removes all nodes and edges belonging to files present in the
changedFileslist, eliminating stale relationships and definitions - Injecting fresh data – It merges the newly generated nodes and edges from the current analysis into the existing graph structure
- Updating metadata – It persists the new
gitCommitHashandanalyzedAttimestamps to prevent redundant future analyses
Under the hood, the new analysis data originates from the GraphBuilder class defined in understand-anything-plugin/packages/core/src/analyzer/graph-builder.ts (lines 60-138). This builder constructs comprehensive nodes representing files, functions, classes, and imports while recording semantic relationships such as "contains" and "imports" edges that form the knowledge graph's structure.
Structural Change Detection and Fingerprinting
For scenarios requiring granularity beyond file-level changes, the system implements content fingerprinting to distinguish between superficial and meaningful modifications.
The Fingerprint Subsystem
The fingerprint.ts module provides buildFingerprintStore and compareFingerprints utilities that compute structural fingerprints for each file. Rather than relying solely on Git's text diff, this subsystem:
- Generates content hashes for rapid change detection
- Extracts structural signatures including function signatures, class members, and import/export declarations
- Classifies modifications into three categories:
- NONE – No changes detected
- COSMETIC – Whitespace, comments, or formatting changes that don't affect program semantics
- STRUCTURAL – Modifications to APIs, function signatures, or class hierarchies that impact dependent code
The compareFingerprints function (lines 24-46 in fingerprint.ts) performs this classification by comparing the current file's fingerprint against the stored baseline. Meanwhile, buildFingerprintStore (lines 67-140) maintains the registry of these fingerprints indexed by commit hash. This granular detection allows the merge step to retain nodes for files with purely cosmetic changes while rebuilding only those with structural impact.
Practical Implementation Examples
The following code demonstrates how to integrate these APIs into custom analysis pipelines.
Detecting Staleness and Merging Updates
import { isStale, mergeGraphUpdate } from "@understand-anything/core";
import { GraphBuilder } from "@understand-anything/core";
import { analyzeProject } from "./my-analyzer"; // your analysis pipeline
const projectDir = "/my/project";
const lastCommit = "a1b2c3d4"; // stored from previous run
// 1️⃣ Find changed files
const { stale, changedFiles } = isStale(projectDir, lastCommit);
if (!stale) {
console.log("Knowledge graph is up‑to‑date");
return;
}
// 2️⃣ Run analysis only on the changed files
const { newNodes, newEdges, newCommitHash } = await analyzeProject(projectDir, changedFiles);
// 3️⃣ Load the previous graph (e.g. from .understand-anything/knowledge-graph.json)
const existingGraph = await loadGraph();
// 4️⃣ Merge the updates
const merged = mergeGraphUpdate(
existingGraph,
changedFiles,
newNodes,
newEdges,
newCommitHash,
);
// 5️⃣ Persist the merged graph
await saveGraph(merged);
Using the Fingerprint API for Detailed Analysis
import { buildFingerprintStore, analyzeChanges } from "@understand-anything/core";
import { PluginRegistry } from "@understand-anything/core";
const registry = new PluginRegistry(); // registers all language extractors
const allFiles = await listProjectFiles(projectDir); // e.g. via glob
// Load previous fingerprint store (if any)
const oldStore = await loadFingerprintStore();
// Re‑build store for the current HEAD
const newStore = buildFingerprintStore(projectDir, allFiles, registry, "HEAD");
// Determine which files actually changed
const changed = getChangedFiles(projectDir, oldStore.gitCommitHash);
// Get a rich analysis
const report = analyzeChanges(projectDir, changed, oldStore, registry);
console.log(report);
Summary
Egonex-AI's incremental analysis architecture delivers efficient code knowledge graph maintenance through several coordinated mechanisms:
- Git-based detection via
getChangedFilesidentifies added, removed, and modified files by comparing commit hashes - Staleness evaluation through
isStaleprovides a boolean gate that prevents unnecessary rebuilds - Surgical graph updates using
mergeGraphUpdateprune obsolete nodes and integrate fresh analysis while preserving unchanged data - Structural fingerprinting distinguishes cosmetic from structural changes, optimizing the rebuild scope
- Metadata tracking ensures subsequent runs can determine freshness without re-analyzing unchanged repositories
Frequently Asked Questions
How does Egonex-AI determine which files have changed?
The system relies on the getChangedFiles function in staleness.ts to execute a git diff between the last analyzed commit hash stored in the graph metadata and the current HEAD. This returns a definitive list of file paths that have been added, removed, or modified since the previous analysis, using Git as the authoritative change tracking mechanism.
What is the difference between COSMETIC and STRUCTURAL changes?
COSMETIC changes include whitespace adjustments, comment modifications, or formatting changes that don't alter program behavior, while STRUCTURAL changes affect function signatures, class members, import/export statements, or other semantic elements that impact how code interacts. The fingerprint system in fingerprint.ts uses compareFingerprints to classify changes accordingly, allowing the pipeline to skip rebuilding nodes for purely cosmetic modifications.
How does mergeGraphUpdate preserve existing graph data?
The mergeGraphUpdate function (lines 54-90 in staleness.ts) first identifies and removes only the nodes and edges associated with files present in the changed files list, then injects the newly analyzed nodes and edges into the existing graph structure. This surgical approach maintains all relationships and metadata for unchanged files while updating only the affected regions, significantly reducing computational overhead compared to full graph regeneration.
Can the incremental analysis work without Git?
While the primary implementation in staleness.ts depends on Git for change detection via git diff, the architecture separates concerns through the isStale and mergeGraphUpdate interfaces. Custom implementations could theoretically replace the Git-based getChangedFiles logic with filesystem watchers or timestamp comparisons, though the official Egonex-AI distribution requires Git to function correctly.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →