fix: support monorepo/multi-repo workspaces with nested git sub-projects#2369
fix: support monorepo/multi-repo workspaces with nested git sub-projects#2369can2049 wants to merge 2 commits into
Conversation
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>
67e65cb to
fe9c1dd
Compare
|
How did you test this? |
sinelaw
left a comment
There was a problem hiding this comment.
This is an autogenerated comment
Interactive testing performed: Opened fresh from /tmp/monorepo-test (non-git root with project-a/ and project-b/ sub-repos). File explorer immediately showed ● decorations on both sub-dirs; expanding project-a revealed file-a.txt M and new-file.txt U — git status propagation works. Verified at CLI level that git blame from the file's own directory succeeds while it fails from the monorepo root (the core of this fix).
Issues that need to be addressed before merge:
-
Missing tests (CONTRIBUTING.md §Testing rule 1): Every behavioral claim must be backed by a test that fails without the change. No automated tests are added. The project has a rich e2e harness (
crates/fresh-editor/tests/e2e/) withGitTestRepohelpers already used for monorepo scenarios (seepackage_manager.rs). Please add at least one e2e test: open a workspace whose root is not a git repo but contains a sub-repo, verify git status decorations appear, and verify git blame works on a file in the sub-repo. -
Direct
std::fsinfile_operations.rs(CONTRIBUTING.md §Code Guidelines rule 4): The new BFS inresolve_git_index(line 968) usesstd::fs::read_dir(&dir)andpath.is_dir()/dot_git.exists()directly. The file already imports and uses theFileSystemtrait (self.authority().filesystem) for all other I/O. This bypasses SSH/remote support. Please rewrite usingself.authority().filesystem.read_dir()and.metadata()instead. -
node_modulesnot excluded from sub-repo discovery:discoverSubReposingit_explorer.tsskips hidden dirs (.-prefixed) but notnode_modules, which is common in JS monorepos and can be large. Add|| entry.name === 'node_modules'to the skip condition.
Generated by Claude Code
…add e2e test - Replace direct std::fs calls in resolve_git_index BFS with self.authority().filesystem (read_dir/exists) to support SSH/remote - Exclude node_modules from sub-repo discovery in both Rust and TS - Add e2e test verifying git decorations in non-git workspace root with multiple git sub-projects Co-authored-by: Cursor <cursoragent@cursor.com>
|
Thanks for the thorough review! All three issues have been addressed in the latest push:
|
sinelaw
left a comment
There was a problem hiding this comment.
Nice direction — single-repo behavior is unchanged and monorepos gain a lot. Tested a debug build against a monorepo fixture (/workspace not a repo, project-a/ a real sub-repo, active buffer inside it). Two things should be fixed before merge; the rest is fine as follow-up.
1. git_find_file.ts is broken in a monorepo (blocker). Running "Git Find File" opened an empty buffer instead of the tracked file. Cause: cwd is set to the file's directory, so git ls-files returns paths relative to that dir (e.g. app.txt), which openFile then resolves against the workspace root → a non-existent path. Even at the repo root it'd return src/app.txt and open the wrong file. Fix: resolve ls-files output against the repo root before opening (or revert this file).
2. git_log.ts / code-tour.ts — the editor.getCwd() arg is a no-op (please revert). The backend already defaults cwd to the working dir, so these change nothing. In a monorepo "Git Log" just reports "No commits found or not a git repository" (commit loading also uses the default cwd), so the "passes explicit cwd for git show" change has no effect. Reverting keeps the diff honest; real monorepo support for git_log is a separate change.
For contrast, git_blame works correctly in the same setup because it derives cwd from the file path — that's the right pattern; find-file/git_log just apply it wrong.
Non-blocking follow-ups: the "active-buffer dir → getCwd" helper is duplicated in ~4 plugins (consolidate into lib/git_history.ts); the TS discoverSubRepos and Rust BFS reimplement the same depth-3 discovery (worth unifying behind one cached core resolver); and resolve_git_index only watches the first sub-repo's index, so decorations in other sub-repos won't auto-refresh (limitation, not a regression — a code comment would help).
Generated by Claude Code
|
One more, for the follow-up: repo detection should trust Generated by Claude Code |
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:
This PR fixes all git-related functionality to work correctly in monorepo setups.
Root Causes
Symlink handling in
readDir: Theeditor.readDir()backend usedstd::fs::DirEntry::file_type()which does not follow symlinks. In monorepos,.gitis often a symlink to the actual git directory — it was incorrectly reported asis_dir=false, preventing plugins from detecting git repos.Missing
cwdin git command execution: Most plugins callededitor.spawnProcess("git", args)without specifying acwdparameter, 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.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)
quickjs_backend.rsread_dirnow follows symlinks viastd::fs::metadata()to correctly reportis_dirfor.gitsymlinksfile_operations.rsresolve_git_indexuses BFS with max depth 3 to discover nested sub-reposPlugins (TypeScript)
git_explorer.tsdiscoverSubRepos(dir, maxDepth=3)for multi-level repo discoverygit_statusbar.tsgetGitCwd()helper — prioritizes active buffer's directory for branch detectiongit_blame.tseditor.pathDirname(filePath)as cwd forgit blameandgit showgit_find_file.tsgit ls-filesgit_log.tsgit showlive_grep.tsmerge_conflict.tsgit show :0:pathcode-tour.tsgit rev-parseaudit_mode.tsstate.repoRoot || editor.getCwd()as cwdDesign Decisions
rev-parse --show-toplevel..gitboundaries (won't enter a git repo to find submodules — those are managed by git itself)..are skipped during discovery to avoid scanning.git,.cache,node_modulesetc.Test Plan
Made with Cursor