feat(ui): repo scan discovers nested repos in subfolders
RepoScanner.Scan now recurses into subdirectories (max depth 5) instead of only checking the immediate children of the chosen folder. A found repo (.git as dir or file) is added without descending further; the selected folder itself is checked too. Skips node_modules/bin/obj/.git/ .vs/packages and reparse points (junctions/symlinks). Per-directory IOException/UnauthorizedAccessException no longer aborts the whole scan.
This commit is contained in:
@@ -4,23 +4,63 @@ public sealed record RepoCandidate(string Name, string FullPath);
|
||||
|
||||
public static class RepoScanner
|
||||
{
|
||||
private const int MaxDepth = 5;
|
||||
|
||||
private static readonly HashSet<string> SkipDirNames = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"node_modules", "bin", "obj", ".git", ".vs", "packages",
|
||||
};
|
||||
|
||||
public static IReadOnlyList<RepoCandidate> Scan(string parentFolder)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(parentFolder) || !Directory.Exists(parentFolder))
|
||||
return Array.Empty<RepoCandidate>();
|
||||
|
||||
var result = new List<RepoCandidate>();
|
||||
IEnumerable<string> subdirs;
|
||||
try { subdirs = Directory.EnumerateDirectories(parentFolder); }
|
||||
catch (Exception e) when (e is IOException or UnauthorizedAccessException)
|
||||
{ return Array.Empty<RepoCandidate>(); }
|
||||
|
||||
foreach (var dir in subdirs)
|
||||
{
|
||||
var gitPath = Path.Combine(dir, ".git");
|
||||
if (Directory.Exists(gitPath) || File.Exists(gitPath))
|
||||
result.Add(new RepoCandidate(Path.GetFileName(dir), dir));
|
||||
}
|
||||
ScanDirectory(parentFolder, depth: 0, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void ScanDirectory(string dir, int depth, List<RepoCandidate> result)
|
||||
{
|
||||
if (IsRepo(dir))
|
||||
{
|
||||
result.Add(new RepoCandidate(Path.GetFileName(dir), dir));
|
||||
return;
|
||||
}
|
||||
|
||||
if (depth >= MaxDepth)
|
||||
return;
|
||||
|
||||
IEnumerable<string> subdirs;
|
||||
try { subdirs = Directory.EnumerateDirectories(dir); }
|
||||
catch (Exception e) when (e is IOException or UnauthorizedAccessException)
|
||||
{ return; }
|
||||
|
||||
foreach (var subdir in subdirs)
|
||||
{
|
||||
var name = Path.GetFileName(subdir);
|
||||
if (SkipDirNames.Contains(name))
|
||||
continue;
|
||||
|
||||
try
|
||||
{
|
||||
var info = new DirectoryInfo(subdir);
|
||||
if (info.Attributes.HasFlag(FileAttributes.ReparsePoint))
|
||||
continue;
|
||||
}
|
||||
catch (Exception e) when (e is IOException or UnauthorizedAccessException)
|
||||
{ continue; }
|
||||
|
||||
try { ScanDirectory(subdir, depth + 1, result); }
|
||||
catch (Exception e) when (e is IOException or UnauthorizedAccessException)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsRepo(string dir)
|
||||
{
|
||||
var gitPath = Path.Combine(dir, ".git");
|
||||
return Directory.Exists(gitPath) || File.Exists(gitPath);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user