Repository navigation
fix(status): a project below its repository root sees its uncommitted changes - #2932
Merged
Merged
Conversation
colbymchenry
force-pushed
the
claude/fix-status-subdir-project
branch
from
October 27, 2026 18:28
8161073 to
76072ed
Compare
… changes When the indexed project is a folder of its git repository (a monorepo package, an app/ folder beside a server/ one), `codegraph status`, `status --json` and the freshness check behind codegraph_status reported the index up to date while `codegraph sync` found the very same changes. `git status --porcelain` prints paths from the repository root whatever folder it runs in, and reports the whole repository. Change detection joined `app/src/index.js` under the project, found nothing at `app/app/src/index.js`, and dropped it. The dirty paths a sync records for the restore check (from PR #1878) were stored the same way, so a file put back with git after a sync was missed too. And a project in a folder its repository gitignores got a permanently clean answer from git, which says nothing about an ignored folder. Change detection now asks where the project sits in its repository (`git rev-parse --show-prefix`, kept per folder, and skipped when the folder has its own .git). Below the root it runs git status with a `-- .` pathspec, takes the prefix off every path and leaves out anything outside the folder. The project's ignore rules match those project-relative paths, the recorded dirty paths are project-relative, and a project its repository ignores declines the git path as the scan already did, so callers take the full scan. A project at its repository root runs exactly the git command it ran before. Validation: - git-status-subdir-project.test.ts (new, 7 cases): edited, added and deleted files in a subdir project, matching sync; edits outside the project left out, with the pathspec asserted; the restore case, across a reopen; the project's own anchored .gitignore rule; an embedded repo inside the project; the gitignored-project fallback; and a repo-root project running exactly today's status command with no prefix call. 6 fail on main's source; the repo-root control passes on both. Four mutations (no pathspec, ignore rules on repo-relative paths, no fallback, no .git shortcut) each fail the test. - CLI repros: after an edit and an added file in app/, status --json reads 0/0/0 on main and 1 added + 1 modified with the fix; the same in a gitignored project; a subdir restore reads 0 on main and 1 modified with the fix (a repo-root project reads 1 on both). - The MCP freshness worker on a subdir project: 0/0/0 on main, 1 added + 1 modified with the fix. Reported in PR #1636 by @innoxxicide. This reimplements the fix independently, on top of the restore check that landed after it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
colbymchenry
force-pushed
the
claude/fix-status-subdir-project
branch
from
October 27, 2026 18:40
76072ed to
6e9834e
Compare
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
When the indexed project is a folder of its git repository (a monorepo package, an
app/folder beside aserver/one),codegraph status,status --jsonand the freshness line ofcodegraph_statussaid the index was up to date whilecodegraph syncfound the very same edits, new files and deletions. They now report them. A file put back with git after a sync now shows as pending too, and a project in a folder that its repository's.gitignoreleaves out is checked by the full scan instead of always reading as unchanged.Reported in #1636 by @innoxxicide; this reimplements the fix independently, on current main (which since gained the restore check from #1878 and the committed-changes half from #1829).
Root cause
collectGitStatusrunsgit status --porcelain -z -uallin the project folder. Porcelain output is the same from any folder: paths run from the repository root, and the report covers the whole repository. For a project inapp/,getChangedFilesjoinedapp/src/index.jsunder the project, found nothing atapp/app/src/index.jsand dropped the entry, so every working-tree change disappeared. Three things followed:indexed_dirty_paths) were stored repository-relative, so a file restored with git after a sync was never re-checked.getGitVisibleFiles) already declined there and walked the disk; change detection had no such fallback.The committed half (
git diff --relative -- ., #1829) was already project-relative.Fix
gitRepoPrefix(dir): where the folder sits in its repository (git rev-parse --show-prefix), kept per folder for the process's life. A folder with its own.gitis a repository or worktree root and costs no git call at all.git statusgets a-- .pathspec, so git reports only the project's folder. The prefix comes off every path, anything outside the folder is left out, and the ignore rules match the resulting project-relative paths. Embedded repos found under the project are repository roots and recurse with no prefix, as before.indexed_dirty_pathscomes from the same function, so it is project-relative now, and the restore check works for a subdir project. An index written by an older build may hold repository-relative dirty paths: they point at nothing, are skipped, and the next sync or index rewrites them.git check-ignore) moved into one helper that the scan and change detection both use. When it answers yes,getGitChangedFilesreturns null and callers take the full scan that indexed the project.codegraph status, which now opens read-only (fix(cli): codegraph status opens the index read-only, and says when a rebuild is running #2930). The dirty paths are still recorded only by index and sync.Cost: a project below its repository root adds one
rev-parse --show-prefix(about 29 ms here; once per process, but once per measurement in the MCP freshness worker, which starts fresh each time) and onecheck-ignore(about 29 ms) per change check. A repo-root project adds oneexistsSync.Validation
__tests__/git-status-subdir-project.test.ts(new, 7 cases):getGitChangedFilesandgetChangedFiles, thensyncagrees and status reads clean;-- .pathspec;git checkoutreads as modified, also after a reopen, and a sync clears it;.gitignore(an anchored/generated/rule over a tracked file) is matched against project-relative paths;git status --porcelain --no-renames -z -uall, with no--show-prefixcall over the index and status lifecycle and nocheck-ignoreduring status.Red: 6 of 7 fail on main's
src/extraction/index.ts. The repo-root control passes on both, as it should.Mutations, each caught by the test: no pathspec; ignore rules matched on repository-relative paths; no gitignored fallback; no
.gitshortcut.Existing related files pass:
git-*,status-*,mcp-status-freshness,mcp-freshness-bounds,worktree*(11 files, 85 tests).CLI, with the triage repro scripts, main 7ff2841 vs this branch:
app/project: edit + new file,status --jsonsyncapp/onlyapp/gitignored by the repository: edit + new filegit checkoutthe file: repo-root projectapp/projectThe MCP freshness worker (what
codegraph_statusreports as "Changes since index") on anapp/project after an edit and a new file:0/0/0on main,1 added, 1 modifiedwith the fix.Full suite on the pushed commit 6e9834e (main 0174f80), with the kernel and
CODEGRAPH_NO_DAEMONunset: 770 files, 9,089 passed, 0 failed, 85 skipped.Left out
.gitis picked up at once by the.gitcheck. A repository created between the project and its old root while the process runs is not; that one needs a restart.🤖 Generated with Claude Code