How the Incremental Update Feature in code-review-graph Uses SHA‑256 Hash Checking
The incremental update feature in code-review-graph avoids redundant parsing by computing a SHA‑256 hash of each source file’s raw bytes and comparing it to a stored checksum, skipping files that haven’t changed since the last build.
The code-review-graph repository provides a static analysis graph builder that must efficiently handle large codebases during iterative development. By implementing content-based addressing via SHA‑256, the incremental update feature in code-review-graph eliminates wasted CPU cycles on unchanged files while ensuring the graph accurately reflects only modified source code.
How SHA‑256 Hash Generation Works in the Parser
Every file processed by the system—whether during an initial full build or a subsequent incremental update—triggers a cryptographic hash computation to establish its identity.
Computing Checksums in _parse_single_file
When the parallel executor invokes _parse_single_file, the file is read as raw bytes and digested using Python’s hashlib module. This occurs at lines 1089–1092 in code_review_graph/incremental.py:
raw = abs_path.read_bytes()
fhash = hashlib.sha256(raw).hexdigest()
This fhash value becomes the canonical identifier for the file’s current content state.
Serial Fallback in incremental_update
The same hashing logic appears in the serial fallback path within the incremental_update function at lines 1060–1062, ensuring consistency regardless of whether files are processed in parallel or sequentially:
raw = abs_path.read_bytes()
fhash = hashlib.sha256(raw).hexdigest()
Storing and Retrieving File Hashes in the Graph Database
After parsing, the hash persists alongside the extracted graph data. The GraphStore class defined in code_review_graph/graph.py manages this through store.store_file_nodes_edges(...), which writes the SHA‑256 digest to the file_hash column on Node objects.
During an incremental run, the system retrieves the previously stored hash via store.get_nodes_by_file(str(abs_path)) to perform the comparison.
The Skip Logic: Avoiding Unnecessary Reparsing
The core optimization occurs when incremental_update evaluates whether a candidate file actually requires reprocessing. At lines 1062–1065 in code_review_graph/incremental.py, the code compares the computed hash against the database record:
existing_nodes = store.get_nodes_by_file(str(abs_path))
if existing_nodes and existing_nodes[0].file_hash == fhash:
continue # unchanged → skip re‑parsing
This guard clause short-circuits the parsing pipeline for any file whose content hash matches the stored value. The workflow proceeds as follows:
- The VCS (Git or SVN) supplies a list of changed files based on commit metadata.
- The system locates dependent files by traversing edges in the graph’s edge table.
- For each candidate, the SHA‑256 checksum is recomputed from raw bytes.
- Files with matching hashes are omitted from the parsing step, dramatically reducing CPU and I/O overhead.
Complete Example: Full Build vs Incremental Update
The following Python script demonstrates the hash-checking workflow in practice. It performs an initial full build to establish baseline hashes, then runs an incremental update that leverages SHA‑256 comparisons to skip unchanged files:
from pathlib import Path
from code_review_graph.incremental import incremental_update, full_build
from code_review_graph.graph import GraphStore
repo_root = Path("/path/to/repo")
store = GraphStore() # initialize the SQLite graph store
# Full build – hashes are stored for every parsed file
full_build(repo_root, store)
# Later, after a few commits, run an incremental update.
# Only files whose SHA‑256 digest differs from the stored value are reparsed.
result = incremental_update(repo_root, store)
print("Files reparsed:", result["files_updated"])
print("Skipped files because hash matched:", len(result["changed_files"]) - result["files_updated"])
Key Implementation Files
code_review_graph/incremental.py– Implements theincremental_updatelogic, computes SHA‑256 hashes at lines 1060–1092, and decides whether to re‑parse a file.code_review_graph/graph.py– DefinesGraphStoreand the storage schema that records thefile_hashfor each node.code_review_graph/parser.py– ProvidesCodeParser.parse_bytes, which processes the raw bytes and returns AST nodes and edges consumed by the hash‑checking workflow.
Summary
- The incremental update feature in code-review-graph computes a SHA‑256 hex digest of every source file’s raw bytes before parsing.
- Hashes are stored in the SQLite graph database under the
file_hashcolumn onNodeobjects viaGraphStore. - The skip logic in
incremental_updatecompares the computed hash against the stored value at lines 1062–1065, bypassing parsing for unchanged files. - This mechanism supports VCS-agnostic incremental builds by verifying actual content changes rather than relying solely on file timestamps.
Frequently Asked Questions
Why does code-review-graph use SHA‑256 instead of MD5 or SHA‑1?
SHA‑256 provides a larger hash space and stronger collision resistance than MD5 or SHA‑1, ensuring that the incremental update feature correctly distinguishes between files even in large repositories. While computationally slightly heavier, the difference is negligible for source code files and prevents rare but costly parsing errors from hash collisions.
Where is the file hash stored in the graph database?
According to the schema in code_review_graph/graph.py, the SHA‑256 digest is stored in the file_hash column on Node objects. The GraphStore class persists this value when calling store_file_nodes_edges() after successfully parsing a file.
How does the incremental update handle file deletions?
While the SHA‑256 hash checking focuses on modified content, the incremental_update routine also processes deletions reported by the VCS. When a file no longer exists on disk but exists in the graph database, the system removes its associated nodes and edges, ensuring the graph reflects the current repository state regardless of hash values.
Can the hash checking be disabled for performance testing?
The source code does not expose a configuration flag to disable SHA‑256 checks, as they are fundamental to the incremental_update logic. However, developers can force a full rebuild by calling full_build() instead of incremental_update(), which ignores stored hashes and reparses every file in the repository.
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 →