Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 34 additions & 23 deletions packages/core/src/utils/gitIgnoreParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export class GitIgnoreParser implements GitIgnoreFilter {
private projectRoot: string;
private ig: Ignore = ignore();
private patterns: string[] = [];
private readonly maxScannedDirs = 200;

constructor(projectRoot: string) {
this.projectRoot = path.resolve(projectRoot);
Expand All @@ -33,34 +34,44 @@ export class GitIgnoreParser implements GitIgnoreFilter {
this.findAndLoadGitignoreFiles(this.projectRoot);
}

private findAndLoadGitignoreFiles(dir: string): void {
const relativeDir = path.relative(this.projectRoot, dir);
private findAndLoadGitignoreFiles(startDir: string): void {
const queue: string[] = [startDir];
let scannedDirs = 0;
let queueHead = 0;

// For sub-directories, check if they are ignored before proceeding.
// The root directory (relativeDir === '') should not be checked.
if (relativeDir && this.isIgnored(relativeDir)) {
return;
}
while (queueHead < queue.length && scannedDirs < this.maxScannedDirs) {
const dir = queue[queueHead];
queueHead++;
scannedDirs++;

// Load patterns from .gitignore in the current directory
const gitignorePath = path.join(dir, '.gitignore');
if (fs.existsSync(gitignorePath)) {
this.loadPatterns(path.relative(this.projectRoot, gitignorePath));
}
const relativeDir = path.relative(this.projectRoot, dir);

// Recurse into subdirectories
try {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
if (entry.name === '.git') {
continue;
}
if (entry.isDirectory()) {
this.findAndLoadGitignoreFiles(path.join(dir, entry.name));
// For sub-directories, check if they are ignored before proceeding.
// The root directory (relativeDir === '') should not be checked.
if (relativeDir && this.isIgnored(relativeDir)) {
continue;
}

// Load patterns from .gitignore in the current directory
const gitignorePath = path.join(dir, '.gitignore');
if (fs.existsSync(gitignorePath)) {
this.loadPatterns(path.relative(this.projectRoot, gitignorePath));
}

// Recurse into subdirectories
try {
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
if (entry.name === '.git') {
continue;
}
if (entry.isDirectory()) {
queue.push(path.join(dir, entry.name));
}
Comment on lines +68 to +70
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation of the breadth-first search can lead to excessive memory consumption in repositories with directories containing a very large number of subdirectories. The queue array can grow to a very large size before the scannedDirs < this.maxScannedDirs condition in the while loop is met, as all subdirectories of a given directory are added to the queue in one go. This could introduce a memory-related performance issue, which is contrary to the goal of this pull request.

To prevent this, I suggest capping the size of the queue to avoid it growing uncontrollably. By stopping the addition of new directories to the queue when its size approaches maxScannedDirs, we can effectively limit memory usage.

          if (entry.isDirectory()) {
            if (queue.length >= this.maxScannedDirs) {
              break;
            }
            queue.push(path.join(dir, entry.name));
          }

}
} catch (_error) {
// ignore readdir errors
}
} catch (_error) {
// ignore readdir errors
}
}

Expand Down
Loading