Skip to content

fix(incremental): reconcile reverted file content - #860

Open
yzxcj797 wants to merge 2 commits into
tirth8205:mainfrom
yzxcj797:fix/incremental-reverted-content
Open

fix(incremental): reconcile reverted file content#860
yzxcj797 wants to merge 2 commits into
tirth8205:mainfrom
yzxcj797:fix/incremental-reverted-content

Conversation

@yzxcj797

Copy link
Copy Markdown
Contributor

Problem

An incremental update discovers work solely from the VCS diff. If a file is edited, indexed, and then reverted to bytes identical to the diff base, that file disappears from the next diff. The graph keeps nodes and edges from the intermediate content until an unrelated event causes that path to be reparsed.

This also happens when another file keeps the diff non-empty: the reverted file is simply absent from the changed-path set and is never considered.

Fix

  • Expose the file hashes already stored on File nodes.
  • Before dispatching incremental work, compare those hashes with current file bytes and merge mismatched paths into the changed-file set.
  • The existing per-file hash check and atomic replacement then purge the intermediate nodes and replace them with the reverted content.

Hash comparison is independent of git history, so it works regardless of which valid diff base the caller selects.

Tests

  • Added a direct edit/revert regression: an intermediate function is indexed, then removed after an otherwise-empty update.
  • Added a mixed-path regression: a reverted file is reconciled even when another changed file keeps the update non-empty.
  • Verified the first regression fails on main and passes with this change.

Closes #817

Compare indexed file hashes with current bytes so an edit followed by a revert is reparsed even when the git diff is empty or another file keeps it non-empty.

Closes tirth8205#817
@tirth8205

Copy link
Copy Markdown
Owner

The fix itself is sound and the regressions are valid; I confirmed both new tests fail on main. My concern is where the scan runs. _find_content_mismatches (incremental.py:711) is invoked unconditionally at the top of incremental_update, so it stats, reads, and sha256s every indexed file on every call. On the CLI path _reconcile_stale_files already does a full scan, so there it roughly doubles read cost (103ms to 170ms no-op on a 2000-file repo here). Watch mode is the real problem: batches call incremental_update with reconcile_stale=False precisely to stay proportional to events, and this adds a full-repo read to every debounced batch (0ms to 62ms at 2000 files, linear in repo bytes). Watch does not need the scan: a revert generates its own event, so the file is already in the batch and the existing hash check covers it. Please gate the scan on reconcile_stale or a dedicated flag. Optional: pass the computed hashes forward so mismatched files are not read and hashed again in the quick-check loop.

@yzxcj797

Copy link
Copy Markdown
Contributor Author

Thanks for the benchmark. I gated the content scan on reconcile_stale, so watch batches keep their existing event-bounded behavior. The hashes computed by that scan are now reused by the quick-check loop, avoiding a second read/hash for indexed files. I also added a regression test proving the scan is skipped when reconciliation is disabled.

@github-actions

Copy link
Copy Markdown

code-review-graph review

Overall risk: 0.60 (MEDIUM) — 9 changed function(s)/class(es), 0 affected flow(s), 4 test gap(s)

Risk-scored changes

Risk Level Symbol Location Tested
0.60 medium code_review_graph/incremental.py::_find_content_mismatches code_review_graph/incremental.py:711 no
0.55 medium code_review_graph/incremental.py::incremental_update code_review_graph/incremental.py:1198 yes
0.50 medium code_review_graph/graph.py::GraphStore.get_file_hashes code_review_graph/graph.py:1202 no
0.30 low code_review_graph/graph.py::GraphStore code_review_graph/graph.py:185 yes
0.30 low tests/test_incremental.py::TestRevertedContentParity.fake_content_scan tests/test_incremental.py:1826 no
0.05 low tests/test_incremental.py::TestRevertedContentParity tests/test_incremental.py:1818 no
0.05 low tests/test_incremental.py::TestRevertedContentParity.test_content_scan_runs_only_for_reconciling_updates tests/test_incremental.py:1821 (test)
0.05 low tests/test_incremental.py::TestRevertedContentParity.test_reverted_file_with_empty_diff_is_reparsed tests/test_incremental.py:1860 (test)
0.05 low tests/test_incremental.py::TestRevertedContentParity.test_reverted_file_is_reconciled_alongside_another_change tests/test_incremental.py:1896 (test)

Test gaps

  • code_review_graph/graph.py::GraphStore.get_file_hashes (code_review_graph/graph.py:1202)
  • code_review_graph/incremental.py::_find_content_mismatches (code_review_graph/incremental.py:711)
  • tests/test_incremental.py::TestRevertedContentParity (tests/test_incremental.py:1818)
  • tests/test_incremental.py::TestRevertedContentParity.fake_content_scan (tests/test_incremental.py:1826)

Token savings: this graph-backed report used ~55,534 fewer tokens (~96%) than reading every changed file in full (estimated, chars/4 approximation).


Powered by code-review-graph — local-first analysis; no code leaves the CI runner.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incremental update leaves stale nodes when a file is edited then reverted to match base (empty diff, never reparsed)

2 participants