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
16 changes: 16 additions & 0 deletions src/core/output/outputSort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,22 @@ export const sortOutputFiles = async (
return sortFilesByChangeCounts(files, fileChangeCounts);
};

/**
* Pre-fetch git file change counts so that a later `sortOutputFiles` call
* hits the in-memory cache instead of spawning git subprocesses on the
* critical path.
*/
export const prefetchSortData = async (
config: RepomixConfigMerged,
deps: SortDeps = {
getFileChangeCount,
isGitInstalled,
},
): Promise<void> => {
if (!config.output.git?.sortByChanges) return;
await getFileChangeCounts(config.cwd, config.output.git?.sortByChangesMaxCommits, deps);
};
Comment thread
yamadashy marked this conversation as resolved.

const sortFilesByChangeCounts = (files: ProcessedFile[], fileChangeCounts: Record<string, number>): ProcessedFile[] => {
// Sort files by change count (files with more changes go to the bottom)
return [...files].sort((a, b) => {
Expand Down
11 changes: 10 additions & 1 deletion src/core/packager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import path from 'node:path';
import type { RepomixConfigMerged } from '../config/configSchema.js';
import { logger } from '../shared/logger.js';
import { logMemoryUsage, withMemoryLogging } from '../shared/memoryUtils.js';
import type { RepomixProgressCallback } from '../shared/types.js';
import { collectFiles, type SkippedFileInfo } from './file/fileCollect.js';
Expand All @@ -11,7 +12,7 @@ import type { ProcessedFile } from './file/fileTypes.js';
import { getGitDiffs } from './git/gitDiffHandle.js';
import { getGitLogs } from './git/gitLogHandle.js';
import { calculateMetrics, createMetricsTaskRunner } from './metrics/calculateMetrics.js';
import { sortOutputFiles } from './output/outputSort.js';
import { prefetchSortData, sortOutputFiles } from './output/outputSort.js';
import { produceOutput } from './packager/produceOutput.js';
import type { SuspiciousFileResult } from './security/securityCheck.js';
import { validateFileSafety } from './security/validateFileSafety.js';
Expand Down Expand Up @@ -44,6 +45,7 @@ const defaultDeps = {
createMetricsTaskRunner,
sortPaths,
sortOutputFiles,
prefetchSortData,
getGitDiffs,
getGitLogs,
// Lazy-load packSkill to defer importing the skill module chain
Expand Down Expand Up @@ -77,6 +79,12 @@ export const pack = async (

logMemoryUsage('Pack - Start');

// Pre-fetch git file-change counts for sortOutputFiles while search and
// collection are in flight, so the later sortOutputFiles call is a cache hit.
const sortDataPromise = deps.prefetchSortData(config).catch((error) => {
logger.trace('Failed to prefetch sort data:', error);
});

progressCallback('Searching for files...');
const searchResultsByDir = await withMemoryLogging('Search Files', async () =>
Promise.all(
Expand Down Expand Up @@ -163,6 +171,7 @@ export const pack = async (
// runs twice but is negligible (~1ms for 1000 files). This ordering is required by the
// fast-path in `calculateMetrics`, which walks file contents through the output string
// in order via `extractOutputWrapper`.
await sortDataPromise;
const processedFiles = await deps.sortOutputFiles(filteredProcessedFiles, config);

progressCallback('Generating output...');
Expand Down
Loading