Skip to content

fix(git): fix canonical_root for worktrees and subdirectory projects#672

Closed
anivaryam wants to merge 4 commits into
DeusData:mainfrom
anivaryam:main
Closed

fix(git): fix canonical_root for worktrees and subdirectory projects#672
anivaryam wants to merge 4 commits into
DeusData:mainfrom
anivaryam:main

Conversation

@anivaryam

Copy link
Copy Markdown
Contributor

Summary

Fixes #659detect_changes returns empty impacted_symbols for projects indexed inside git worktrees or as repo subdirectories because canonical_root was computed incorrectly.

Root cause: git rev-parse --git-common-dir outputs a path relative to the directory passed via -C (the indexed path, input_path), not to worktree_root. Two errors in derive_canonical_root():

  1. The relative git_common_dir (e.g. "../.git") was joined with worktree_root instead of input_path. For a project at workspace/scripts/, worktree_root is workspace/ but the relative path is relative to scripts/ — joining it with workspace/ produced workspace/../.git, which strips to workspace/...
  2. Even if joined correctly (scripts/../.git), stripping the /.git suffix leaves scripts/.. — a path with unresolved .. components. Without normalization this is stored as-is and never matches the project's stored root path.

Fix (src/git/git_context.c):

  • Pass input_path (the original indexed directory) as the join base for relative git_common_dir paths.
  • Call realpath() (POSIX) / _fullpath() (Windows) on the joined .git path before stripping the suffix. The .git directory always exists for a valid repository, so the call succeeds and collapses any .. components.

Tests (tests/test_git_context.c):
Three new tests in suite_git_context:

Test plan

  • canonical_root_repo_root passes
  • canonical_root_subdir passes (was failing before fix)
  • canonical_root_linked_worktree passes (was failing before fix)
  • Full make test suite has no regressions

git rev-parse --git-common-dir outputs a path relative to the directory
passed via -C (the indexed path), not to worktree_root. When the indexed
path is a subdirectory of the repo root, or a linked worktree sibling,
joining the relative git_common_dir with worktree_root produced a path
with unresolved ".." components. Stripping the "/.git" suffix then left
e.g. "/workspace/.." rather than "/workspace".

Two changes in derive_canonical_root():
1. Use input_path (the original indexed directory) as the join base for
   relative git_common_dir paths, so "../.git" from scripts/ resolves
   against scripts/, not against the already-resolved worktree root.
2. Call realpath() (POSIX) / _fullpath() (Windows) on the joined .git
   path before stripping the suffix, collapsing any ".." components into
   the canonical filesystem path. The .git directory always exists for a
   valid repository, so realpath() succeeds.

Add test_git_context.c covering all three cases:
- repo indexed from its root (existing behaviour, must not regress)
- project inside a repo, indexed from a subdirectory (issue DeusData#659)
- project in a linked git worktree (issue DeusData#659 primary case)

Fixes DeusData#659

Signed-off-by: anivaryam <anivaryam.dev@gmail.com>
check-no-test-skips.sh prohibits plain SKIP(); git worktree add is
available since git 2.5 (2015) so failure is a real error, not a
platform/version skip.

Signed-off-by: anivaryam <anivaryam.dev@gmail.com>
- Replace MAX_PATH (requires windows.h) with 4096 in both
  src/git/git_context.c and tests/test_git_context.c; use
  sizeof(resolved) instead of the literal for _fullpath()
- Wrap canonical_root_linked_worktree body in #ifdef _WIN32 / #else /
  #endif so realpath() is excluded from Windows compilation
  (SKIP_PLATFORM is a runtime skip — the body still compiled)

Signed-off-by: anivaryam <anivaryam.dev@gmail.com>
@DeusData

Copy link
Copy Markdown
Owner

Huge thanks for opening this PR and for the work you put into it.

The maintainer shop is currently full, so this may sit for a bit before it gets a proper review. We will come back to this as soon as possible with real feedback; I wanted to make sure it did not sit unacknowledged in the meantime.

@DeusData DeusData added bug Something isn't working ux/behavior Display bugs, docs, adoption UX priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. labels Jun 29, 2026
@DeusData

DeusData commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Thanks, this looks focused on #659. Before merge, please add DCO sign-off to the non-merge commit. Also, because the regression test shells out to git, please confirm the command inputs are fixed/test-controlled and note expected CI platform coverage.

@DeusData

DeusData commented Jul 1, 2026

Copy link
Copy Markdown
Owner

Thank you @anivaryam — this is a correct and well-diagnosed fix. You nailed the root cause: git rev-parse --git-common-dir is relative to the -C dir, and the old code joined it against worktree_root and stripped /.git textually, leaving an unresolved .. that never matched the stored root.

I've taken your git_context.c fix verbatim into a distilled follow-up, #749 (354befe), co-authored with you. The only change is the test suite: your canonical_root_repo_root / canonical_root_subdir tests called make_git_repo() (which shells out to git) unconditionally, so the Windows CI leg failed with "failed to init git repo" (ci-ok red) — while the linked-worktree test was already SKIPed there. In the follow-up all three git-based tests SKIP_PLATFORM on Windows (matching that existing convention), so the suite is green cross-platform, and the linked-worktree case is kept as the genuine reproduce-first guard (it's the one that reliably produces a relative --git-common-dir and fails on the unfixed code).

Closing this as superseded by the distilled PR, with full credit to you. Really appreciate the careful work — the fix ships in the next release. 🙏

@DeusData DeusData closed this Jul 1, 2026
pcristin pushed a commit to pcristin/codebase-memory-mcp that referenced this pull request Jul 2, 2026
detect_changes returned an empty impacted_symbols set for projects indexed
inside a git worktree or from a repo subdirectory (DeusData#659): canonical_root was
computed wrong. `git rev-parse --git-common-dir` emits a path relative to the
directory passed via -C (input_path), NOT to worktree_root, and the "/.git"
suffix was stripped textually without resolving ".." components. So e.g.
"../.git" joined against worktree_root, or "/ws/scripts/../.git" strip, left an
unresolved ".." and never matched the project's stored root path.

Fix (src/git/git_context.c): join the relative --git-common-dir against
input_path (the -C dir) instead of worktree_root, and realpath()/_fullpath()-
normalize the result BEFORE stripping "/.git" (the .git dir always exists for a
valid repo, so resolution succeeds). This is consistent with the existing
realpath'd root_path used for prefix matching (mcp.c).

Reproduce-first: tests/test_git_context.c. The subdirectory case is the genuine
guard — a repo indexed from a subdir yields a *relative* --git-common-dir, so the
unfixed code leaves an un-normalized "<root>/subdir/.." (verified RED; GREEN after
the realpath fix). The linked-worktree case is a supporting invariant, not the
DeusData#659 reproducer: git that emits an *absolute* worktree --git-common-dir (e.g.
2.48.x) doesn't manifest the bug, so it passes either way; it still enforces the
worktree->main-root invariant. All three git-based tests SKIP_PLATFORM on Windows
(the CI shell there cannot init a repo via system()).

Distilled from DeusData#672 (thanks @anivaryam); that PR's production fix is taken
verbatim. Its repo_root/subdir tests ran git unconditionally and failed the
Windows CI leg ("failed to init git repo"); here all three git-based tests are
Windows-skipped so ci-ok is green cross-platform. Supersedes DeusData#696, which only
realpath'd after the strip without changing the join base and so still yielded
the workspace parent for the subdirectory case.

Closes DeusData#659

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Co-Authored-By: anivaryam <anivaryam.dev@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority/high Needs near-term maintainer attention; high-impact bug, regression, safety issue, or release blocker. ux/behavior Display bugs, docs, adoption UX

Projects

None yet

Development

Successfully merging this pull request may close these issues.

detect_changes: impacted_symbols always empty for projects inside git worktrees (canonical_root miscalculated)

2 participants