Skip to content

fix: support monorepo/multi-repo workspaces with nested git sub-projects#2369

Open
can2049 wants to merge 1 commit into
sinelaw:masterfrom
can2049:fix/monorepo-git-support
Open

fix: support monorepo/multi-repo workspaces with nested git sub-projects#2369
can2049 wants to merge 1 commit into
sinelaw:masterfrom
can2049:fix/monorepo-git-support

Conversation

@can2049

@can2049 can2049 commented Jun 16, 2026

Copy link
Copy Markdown

Summary

When opening a workspace that is not itself a git repository but contains nested git sub-projects (monorepo/multi-repo layout), fresh failed to:

  • Recognize sub-projects as git repositories
  • Display git status decorations in the file explorer
  • Show correct branch name in the status bar
  • Execute git blame, git grep, git find-file, and other git commands

This PR fixes all git-related functionality to work correctly in monorepo setups.

Root Causes

  1. Symlink handling in readDir: The editor.readDir() backend used std::fs::DirEntry::file_type() which does not follow symlinks. In monorepos, .git is often a symlink to the actual git directory — it was incorrectly reported as is_dir=false, preventing plugins from detecting git repos.

  2. Missing cwd in git command execution: Most plugins called editor.spawnProcess("git", args) without specifying a cwd parameter, causing git commands to execute in the workspace root (which is not a git repo in monorepo setups). This caused all git operations to fail with "not a git repository" errors.

  3. Single-level sub-repo discovery: The file explorer and git index watcher only scanned immediate subdirectories. Multi-level structures like workspace/group/project/ were not discovered.

Changes

Core (Rust)

File Change
quickjs_backend.rs read_dir now follows symlinks via std::fs::metadata() to correctly report is_dir for .git symlinks
file_operations.rs resolve_git_index uses BFS with max depth 3 to discover nested sub-repos

Plugins (TypeScript)

File Change
git_explorer.ts Added recursive discoverSubRepos(dir, maxDepth=3) for multi-level repo discovery
git_statusbar.ts Added getGitCwd() helper — prioritizes active buffer's directory for branch detection
git_blame.ts Passes editor.pathDirname(filePath) as cwd for git blame and git show
git_find_file.ts Uses active buffer's directory as cwd for git ls-files
git_log.ts Passes explicit cwd for git show
live_grep.ts Git-grep availability check falls back to active buffer's directory
merge_conflict.ts Passes file's directory as cwd for git show :0:path
code-tour.ts Passes explicit cwd for git rev-parse
audit_mode.ts All ~20 git spawn calls now use state.repoRoot || editor.getCwd() as cwd

Design Decisions

  • Active buffer context: For per-file operations (blame, status bar, find-file), the file's own directory is used as cwd. Git automatically resolves the containing repository via rev-parse --show-toplevel.
  • Recursive discovery with depth limit: Sub-repo scanning stops at depth 3 and prunes at .git boundaries (won't enter a git repo to find submodules — those are managed by git itself).
  • Hidden directory filtering: Directories starting with . are skipped during discovery to avoid scanning .git, .cache, node_modules etc.
  • Backward compatible: For single-repo workspaces, behavior is unchanged — the explicit cwd resolves to the same directory that was previously used implicitly.

Test Plan

  • Project compiles without errors
  • Open a monorepo workspace (top-level dir with multiple git sub-projects)
  • Verify file explorer shows git status decorations for sub-projects
  • Verify status bar shows correct branch when editing a file in a sub-project
  • Verify git blame works for files in sub-projects
  • Verify git find-file (ls-files) lists files from the active sub-project
  • Verify git grep searches within the active sub-project
  • Verify Review Diff mode works for sub-project changes
  • Open a single-repo workspace and verify all features still work as before

Made with Cursor

When opening a monorepo directory (a top-level directory containing
multiple git sub-projects), fresh failed to recognize sub-projects as
git repositories and all git-dependent features broke.

Root causes:
1. `editor.readDir()` did not follow symlinks — `.git` symlinks
   pointing to directories were reported as is_dir=false.
2. Most plugins called `editor.spawnProcess("git", ...)` without an
   explicit cwd, defaulting to the workspace root which is not itself
   a git repo in monorepo setups.
3. Sub-repo discovery only scanned one level of subdirectories.

Fixes:
- quickjs_backend.rs: `read_dir` now uses `std::fs::metadata()` to
  follow symlinks, correctly reporting is_dir for .git symlinks.
- file_operations.rs: `resolve_git_index` uses BFS (max depth 3) to
  discover nested sub-repos when the workspace root is not a git repo.
- git_explorer.ts: recursive `discoverSubRepos()` (max depth 3) finds
  git repos in nested directory structures.
- git_statusbar.ts: uses active buffer's directory for branch detection.
- git_blame.ts: passes file's directory as cwd for git blame/show.
- git_find_file.ts: uses active buffer's directory for git ls-files.
- git_log.ts: passes explicit cwd for git show.
- live_grep.ts: git-grep availability check falls back to active
  buffer's directory.
- merge_conflict.ts: passes file directory as cwd for git show.
- code-tour.ts: passes explicit cwd for git rev-parse.
- audit_mode.ts: all ~20 git spawn calls now use state.repoRoot as cwd.

Co-authored-by: Cursor <cursoragent@cursor.com>
@can2049 can2049 force-pushed the fix/monorepo-git-support branch from 67e65cb to fe9c1dd Compare June 16, 2026 15:20
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.

1 participant