fix(sync): detect HEAD-moving git operations to prevent stale index#100
Open
andreinknv wants to merge 1 commit into
Open
fix(sync): detect HEAD-moving git operations to prevent stale index#100andreinknv wants to merge 1 commit into
andreinknv wants to merge 1 commit into
Conversation
Sync used to rely solely on `git status --porcelain` for change detection, which only reports working-tree dirtiness vs HEAD. After a `git merge` (or pull, checkout, rebase, reset, post-commit), the working tree is clean and `git status` reports nothing, so sync silently became a no-op while the DB still held pre-operation content hashes. MCP queries then served stale data with no warning. Sync now records the HEAD SHA it was last synced against (in the existing project_metadata table) and, when current HEAD differs, unions `git diff --name-status <last>..HEAD` into the changed-file set. If the recorded HEAD is unreachable (force-push, gc), sync falls back to the filesystem scan path, which is correct regardless of git history state. The same fix is applied to getChangedFiles() so MCP staleness signals stay accurate. Adds 5 regression tests covering merge, branch checkout, committed deletion, unreachable last-synced HEAD, and the no-op clean-tree case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Sync silently leaves the index stale after any HEAD-moving git operation (merge, pull, checkout, rebase, reset, even post-commit) because
git status --porcelainonly reports working-tree dirtiness vs HEAD. When the working tree is clean, sync used to short-circuit and report nothing changed, even though the DB still held pre-operation content hashes. MCP queries then returned stale call graphs and missing or deleted symbols to AI assistants with no warning.What changed
getGitChangedFilesnow also unionsgit diff --name-status <lastSyncedHead>..HEADinto the change set, catching commits that arrived without dirtying the working tree.last_synced_headis recorded in the existingproject_metadatatable after every successfulindexAllandsync. No schema change.needsFullReindex: trueand the caller drops to the filesystem-scan fallback, which is correct regardless of git history state.getChangedFiles()so MCP staleness signals stay accurate.git merge,git checkoutto a branch with diverged content, committed deletion, unreachable last-synced HEAD (amended commit), and the no-op clean-tree sanity case.Affected scenarios (before fix)
Anyone working on a team or across branches:
git pullfrom a remote — every pull leaves the index stalegit checkout <branch>— every branch switchgit rebase/git reset --hardgit commitfollowed by a post-commit-style auto-sync — also affected, since by post-commit time the working tree is cleanSingle-developer single-branch workflows that only edit-then-sync without git operations were unaffected.
Test plan
npm test— 385/385 pass on macOS__tests__/sync.test.tscover the four failure modes plus a clean-tree no-op sanity checkshould trigger sync after file change) is unrelated and passes when the file is run in isolation🤖 Generated with Claude Code