Skip to content
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ node_modules/
# Build output
lib/
website/client/.vitepress/dist/
website/server/dist/

# Logs
*.log
Expand Down
37 changes: 31 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,15 @@
"dependencies": {
"@clack/prompts": "^0.11.0",
"@modelcontextprotocol/sdk": "^1.21.0",
Comment thread
yamadashy marked this conversation as resolved.
"@repomix/tree-sitter-wasms": "^0.1.14",
"@secretlint/core": "^11.2.5",
"@secretlint/secretlint-rule-preset-recommend": "^11.2.5",
"clipboardy": "^5.0.0",
"commander": "^14.0.2",
"fast-xml-parser": "^5.3.1",
"fflate": "^0.8.2",
"git-url-parse": "^16.1.0",
"globby": "^15.0.0",
"globby": "^16.0.0",
"handlebars": "^4.7.8",
"iconv-lite": "^0.7.0",
"istextorbinary": "^9.5.0",
Expand All @@ -100,7 +101,6 @@
"strip-comments": "^2.0.1",
"tiktoken": "^1.0.22",
"tinypool": "^2.0.0",
"@repomix/tree-sitter-wasms": "^0.1.14",
"web-tree-sitter": "^0.25.10",
"zod": "^4.1.12"
},
Expand Down
165 changes: 69 additions & 96 deletions src/core/file/fileSearch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Stats } from 'node:fs';
import fs from 'node:fs/promises';
import path from 'node:path';
import { globby } from 'globby';
import { type Options as GlobbyOptions, globby } from 'globby';
import { minimatch } from 'minimatch';
import type { RepomixConfigMerged } from '../../config/configSchema.js';
import { defaultIgnoreList } from '../../config/defaultIgnore.js';
Expand Down Expand Up @@ -146,32 +146,11 @@ export const searchFiles = async (
}

try {
const [ignorePatterns, ignoreFilePatterns] = await Promise.all([
getIgnorePatterns(rootDir, config),
getIgnoreFilePatterns(config),
]);
const { adjustedIgnorePatterns, ignoreFilePatterns } = await prepareIgnoreContext(rootDir, config);

// Normalize ignore patterns to handle trailing slashes consistently
const normalizedIgnorePatterns = ignorePatterns.map(normalizeGlobPattern);

logger.trace('Ignore patterns:', normalizedIgnorePatterns);
logger.trace('Ignore patterns:', adjustedIgnorePatterns);
logger.trace('Ignore file patterns:', ignoreFilePatterns);

// Check if .git is a worktree reference
const gitPath = path.join(rootDir, '.git');
const isWorktree = await isGitWorktreeRef(gitPath);

// Modify ignore patterns for git worktree
const adjustedIgnorePatterns = [...normalizedIgnorePatterns];
if (isWorktree) {
// Remove '.git/**' pattern and add '.git' to ignore the reference file
const gitIndex = adjustedIgnorePatterns.indexOf('.git/**');
if (gitIndex !== -1) {
adjustedIgnorePatterns.splice(gitIndex, 1);
adjustedIgnorePatterns.push('.git');
}
}

// Start with configured include patterns
let includePatterns = config.include.map((pattern) => escapeGlobPattern(pattern));

Expand Down Expand Up @@ -211,13 +190,8 @@ export const searchFiles = async (
const globbyStartTime = Date.now();

const filePaths = await globby(includePatterns, {
cwd: rootDir,
ignore: [...adjustedIgnorePatterns],
ignoreFiles: [...ignoreFilePatterns],
...createBaseGlobbyOptions(rootDir, config, adjustedIgnorePatterns, ignoreFilePatterns),
onlyFiles: true,
absolute: false,
dot: true,
followSymbolicLinks: false,
}).catch((error: unknown) => {
// Handle EPERM errors specifically
const code = (error as NodeJS.ErrnoException | { code?: string })?.code;
Expand All @@ -239,13 +213,8 @@ export const searchFiles = async (
const emptyDirStartTime = Date.now();

const directories = await globby(includePatterns, {
cwd: rootDir,
ignore: [...adjustedIgnorePatterns],
ignoreFiles: [...ignoreFilePatterns],
...createBaseGlobbyOptions(rootDir, config, adjustedIgnorePatterns, ignoreFilePatterns),
onlyDirectories: true,
absolute: false,
dot: true,
followSymbolicLinks: false,
});

const emptyDirElapsedTime = Date.now() - emptyDirStartTime;
Expand Down Expand Up @@ -292,6 +261,63 @@ export const parseIgnoreContent = (content: string): string[] => {
}, []);
};

/**
* Prepares ignore context including patterns and file patterns with git worktree handling.
* This logic is shared across searchFiles, listDirectories, and listFiles.
*
* @param rootDir The root directory to search
* @param config The merged configuration
* @returns Object containing adjusted ignore patterns and ignore file patterns
*/
const prepareIgnoreContext = async (
rootDir: string,
config: RepomixConfigMerged,
): Promise<{ adjustedIgnorePatterns: string[]; ignoreFilePatterns: string[] }> => {
const [ignorePatterns, ignoreFilePatterns] = await Promise.all([
getIgnorePatterns(rootDir, config),
getIgnoreFilePatterns(config),
]);

// Normalize ignore patterns to handle trailing slashes consistently
const normalizedIgnorePatterns = ignorePatterns.map(normalizeGlobPattern);

// Check if .git is a worktree reference
const gitPath = path.join(rootDir, '.git');
const isWorktree = await isGitWorktreeRef(gitPath);

// Modify ignore patterns for git worktree
const adjustedIgnorePatterns = [...normalizedIgnorePatterns];
Comment thread
yamadashy marked this conversation as resolved.
if (isWorktree) {
// Remove '.git/**' pattern and add '.git' to ignore the reference file
const gitIndex = adjustedIgnorePatterns.indexOf('.git/**');
if (gitIndex !== -1) {
adjustedIgnorePatterns.splice(gitIndex, 1);
adjustedIgnorePatterns.push('.git');
}
}

return { adjustedIgnorePatterns, ignoreFilePatterns };
};

/**
* Creates base globby options with common ignore patterns.
* Returns options that can be extended with specific settings like onlyFiles or onlyDirectories.
*/
const createBaseGlobbyOptions = (
Comment thread
yamadashy marked this conversation as resolved.
rootDir: string,
config: RepomixConfigMerged,
ignorePatterns: string[],
ignoreFilePatterns: string[],
): Omit<GlobbyOptions, 'onlyFiles' | 'onlyDirectories'> => ({
cwd: rootDir,
ignore: ignorePatterns,
gitignore: config.ignore.useGitignore,
Comment thread
yamadashy marked this conversation as resolved.
Comment thread
yamadashy marked this conversation as resolved.
ignoreFiles: ignoreFilePatterns,
absolute: false,
dot: true,
followSymbolicLinks: false,
});
Comment thread
yamadashy marked this conversation as resolved.

export const getIgnoreFilePatterns = async (config: RepomixConfigMerged): Promise<string[]> => {
const ignoreFilePatterns: string[] = [];

Expand All @@ -301,10 +327,9 @@ export const getIgnoreFilePatterns = async (config: RepomixConfigMerged): Promis
//
// Multiple ignore files in the same directory (.gitignore, .ignore, .repomixignore)
// are all merged together. The order in this array does not affect priority.

if (config.ignore.useGitignore) {
ignoreFilePatterns.push('**/.gitignore');
}
//
// .gitignore files are handled by globby's gitignore option (not ignoreFiles)
// to properly respect parent directory .gitignore files, matching Git's behavior.

if (config.ignore.useDotIgnore) {
ignoreFilePatterns.push('**/.ignore');
Expand Down Expand Up @@ -373,37 +398,11 @@ export const getIgnorePatterns = async (rootDir: string, config: RepomixConfigMe
* @returns Array of directory paths relative to rootDir
*/
export const listDirectories = async (rootDir: string, config: RepomixConfigMerged): Promise<string[]> => {
const [ignorePatterns, ignoreFilePatterns] = await Promise.all([
getIgnorePatterns(rootDir, config),
getIgnoreFilePatterns(config),
]);

// Normalize ignore patterns to handle trailing slashes consistently
const normalizedIgnorePatterns = ignorePatterns.map(normalizeGlobPattern);

// Check if .git is a worktree reference
const gitPath = path.join(rootDir, '.git');
const isWorktree = await isGitWorktreeRef(gitPath);

// Modify ignore patterns for git worktree
const adjustedIgnorePatterns = [...normalizedIgnorePatterns];
if (isWorktree) {
// Remove '.git/**' pattern and add '.git' to ignore the reference file
const gitIndex = adjustedIgnorePatterns.indexOf('.git/**');
if (gitIndex !== -1) {
adjustedIgnorePatterns.splice(gitIndex, 1);
adjustedIgnorePatterns.push('.git');
}
}
const { adjustedIgnorePatterns, ignoreFilePatterns } = await prepareIgnoreContext(rootDir, config);

const directories = await globby(['**/*'], {
cwd: rootDir,
...createBaseGlobbyOptions(rootDir, config, adjustedIgnorePatterns, ignoreFilePatterns),
onlyDirectories: true,
absolute: false,
dot: true,
followSymbolicLinks: false,
ignore: [...adjustedIgnorePatterns],
ignoreFiles: [...ignoreFilePatterns],
});

return sortPaths(directories);
Expand All @@ -418,37 +417,11 @@ export const listDirectories = async (rootDir: string, config: RepomixConfigMerg
* @returns Array of file paths relative to rootDir
*/
export const listFiles = async (rootDir: string, config: RepomixConfigMerged): Promise<string[]> => {
const [ignorePatterns, ignoreFilePatterns] = await Promise.all([
getIgnorePatterns(rootDir, config),
getIgnoreFilePatterns(config),
]);

// Normalize ignore patterns to handle trailing slashes consistently
const normalizedIgnorePatterns = ignorePatterns.map(normalizeGlobPattern);

// Check if .git is a worktree reference
const gitPath = path.join(rootDir, '.git');
const isWorktree = await isGitWorktreeRef(gitPath);

// Modify ignore patterns for git worktree
const adjustedIgnorePatterns = [...normalizedIgnorePatterns];
if (isWorktree) {
// Remove '.git/**' pattern and add '.git' to ignore the reference file
const gitIndex = adjustedIgnorePatterns.indexOf('.git/**');
if (gitIndex !== -1) {
adjustedIgnorePatterns.splice(gitIndex, 1);
adjustedIgnorePatterns.push('.git');
}
}
const { adjustedIgnorePatterns, ignoreFilePatterns } = await prepareIgnoreContext(rootDir, config);

const files = await globby(['**/*'], {
cwd: rootDir,
...createBaseGlobbyOptions(rootDir, config, adjustedIgnorePatterns, ignoreFilePatterns),
onlyFiles: true,
absolute: false,
dot: true,
followSymbolicLinks: false,
ignore: [...adjustedIgnorePatterns],
ignoreFiles: [...ignoreFilePatterns],
});

return sortPaths(files);
Expand Down
Loading
Loading