From 51ef9498b3ef097b199387a0fec92f7dda6a6903 Mon Sep 17 00:00:00 2001 From: Orellius Date: Sun, 12 Jul 2026 09:54:03 +0300 Subject: [PATCH] feat: discover repos one level deeper inside grouping folders discover_repos scanned exactly one level under repos_dir, so a layout like code// discovered nothing (the category folders are not git repos). A direct child that is not a repo is now treated as a grouping folder and scanned one more level. Hidden dirs and node_modules are skipped at both levels, and a repo child is never descended into, so nested clones stay out of the list. Activity sorting is unchanged. Co-Authored-By: Claude Fable 5 --- src-tauri/src/lib.rs | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 47006b2..4700027 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -7822,11 +7822,13 @@ fn repo_activity_time(repo: &Path) -> std::time::SystemTime { std::time::SystemTime::UNIX_EPOCH } -/// Walk one level under `dir` and return any subdirectory that contains a -/// `.git` entry. One level is intentional: most people keep their repos -/// directly under a single "code" dir, and recursing deeper would scan node -/// modules / nested clones for no reason. Sorted by most recent activity -/// (newest first) so the repos you actually work in surface at the top. +/// Walk up to two levels under `dir` and return any subdirectory that +/// contains a `.git` entry. A direct child that is itself a repo is taken +/// as-is (never descended into — avoids nested clones); a child that is NOT +/// a repo is treated as a grouping folder (e.g. `code//`) +/// and its children are scanned once more. Hidden dirs and `node_modules` +/// are skipped at both levels. Sorted by most recent activity (newest +/// first) so the repos you actually work in surface at the top. #[tauri::command] fn discover_repos(dir: String) -> Result, String> { let root = PathBuf::from(shellexpand(&dir)); @@ -7838,17 +7840,37 @@ fn discover_repos(dir: String) -> Result, String> { .map(|p| p.root_path) .collect(); let mut out: Vec<(DiscoveredRepo, std::time::SystemTime)> = Vec::new(); - let rd = fs::read_dir(&root).map_err(|e| e.to_string())?; - for entry in rd.flatten() { - let path = entry.path(); - if !path.is_dir() { continue; } - if !path.join(".git").exists() { continue; } - let canon = fs::canonicalize(&path).unwrap_or(path.clone()); + let push_repo = |path: &PathBuf, out: &mut Vec<(DiscoveredRepo, std::time::SystemTime)>| { + let canon = fs::canonicalize(path).unwrap_or(path.clone()); let name = canon.file_name().and_then(|s| s.to_str()).unwrap_or("repo").to_string(); let path_str = canon.to_string_lossy().into_owned(); let already_added = added.contains(&path_str); let activity = repo_activity_time(&canon); out.push((DiscoveredRepo { path: path_str, name, already_added }, activity)); + }; + let skip = |p: &PathBuf| -> bool { + match p.file_name().and_then(|s| s.to_str()) { + Some(n) => n.starts_with('.') || n == "node_modules", + None => true, + } + }; + let rd = fs::read_dir(&root).map_err(|e| e.to_string())?; + for entry in rd.flatten() { + let path = entry.path(); + if !path.is_dir() || skip(&path) { continue; } + if path.join(".git").exists() { + push_repo(&path, &mut out); + continue; + } + // Grouping folder: scan its children for repos (one extra level). + let Ok(rd2) = fs::read_dir(&path) else { continue }; + for e2 in rd2.flatten() { + let p2 = e2.path(); + if !p2.is_dir() || skip(&p2) { continue; } + if p2.join(".git").exists() { + push_repo(&p2, &mut out); + } + } } // Most recent activity first; tie-break by name so the order is stable. out.sort_by(|a, b| b.1.cmp(&a.1)