# How the Incremental Update Feature in code-review-graph Uses SHA‑256 Hash Checking

> Learn how code-review-graph uses SHA-256 hash checking to speed up incremental updates. Avoid redundant parsing by skipping unchanged files and improve your build process.

- Repository: [Tirth Kanani/code-review-graph](https://github.com/tirth8205/code-review-graph)
- Tags: how-to-guide
- Published: 2026-07-22

---

**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`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/incremental.py):

```python
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:

```python
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`](https://github.com/tirth8205/code-review-graph/blob/main/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`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/incremental.py), the code compares the computed hash against the database record:

```python
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:

1. The VCS (Git or SVN) supplies a list of changed files based on commit metadata.
2. The system locates dependent files by traversing edges in the graph’s edge table.
3. For each candidate, the SHA‑256 checksum is recomputed from raw bytes.
4. 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:

```python
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`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/incremental.py)** – Implements the `incremental_update` logic, computes SHA‑256 hashes at lines 1060–1092, and decides whether to re‑parse a file.
- **[`code_review_graph/graph.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/graph.py)** – Defines `GraphStore` and the storage schema that records the `file_hash` for each node.
- **[`code_review_graph/parser.py`](https://github.com/tirth8205/code-review-graph/blob/main/code_review_graph/parser.py)** – Provides `CodeParser.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_hash` column on `Node` objects via `GraphStore`.
- The **skip logic** in `incremental_update` compares 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`](https://github.com/tirth8205/code-review-graph/blob/main/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.