diff --git a/libs/git-utils/src/diff.ts b/libs/git-utils/src/diff.ts index 96609cfa..f052f48d 100644 --- a/libs/git-utils/src/diff.ts +++ b/libs/git-utils/src/diff.ts @@ -24,6 +24,24 @@ function isBinaryFile(filePath: string): boolean { return BINARY_EXTENSIONS.has(ext); } +/** + * Create a synthetic diff for a new file with the given content lines + * This helper reduces duplication in diff generation logic + */ +function createNewFileDiff(relativePath: string, mode: string, contentLines: string[]): string { + const lineCount = contentLines.length; + const addedLines = contentLines.map((line) => `+${line}`).join('\n'); + + return `diff --git a/${relativePath} b/${relativePath} +new file mode ${mode} +index 0000000..0000000 +--- /dev/null ++++ b/${relativePath} +@@ -0,0 +${lineCount === 1 ? '1' : `1,${lineCount}`} @@ +${addedLines} +`; +} + /** * Generate a synthetic unified diff for an untracked (new) file * This is needed because `git diff HEAD` doesn't include untracked files @@ -44,18 +62,19 @@ Binary file ${relativePath} added `; } - // Get file stats to check size + // Get file stats to check size and type const stats = await secureFs.stat(fullPath); + + // Check if it's a directory (can happen with untracked directories from git status) + if (stats.isDirectory()) { + return createNewFileDiff(relativePath, '040000', ['[Directory]']); + } + if (stats.size > MAX_SYNTHETIC_DIFF_SIZE) { const sizeKB = Math.round(stats.size / 1024); - return `diff --git a/${relativePath} b/${relativePath} -new file mode 100644 -index 0000000..0000000 ---- /dev/null -+++ b/${relativePath} -@@ -0,0 +1 @@ -+[File too large to display: ${sizeKB}KB] -`; + return createNewFileDiff(relativePath, '100644', [ + `[File too large to display: ${sizeKB}KB]`, + ]); } // Read file content @@ -90,14 +109,7 @@ ${addedLines}`; // Log the error for debugging logger.error(`Failed to generate synthetic diff for ${fullPath}:`, error); // Return a placeholder diff - return `diff --git a/${relativePath} b/${relativePath} -new file mode 100644 -index 0000000..0000000 ---- /dev/null -+++ b/${relativePath} -@@ -0,0 +1 @@ -+[Unable to read file content] -`; + return createNewFileDiff(relativePath, '100644', ['[Unable to read file content]']); } } diff --git a/libs/git-utils/tests/diff.test.ts b/libs/git-utils/tests/diff.test.ts index 6212df1a..953d2763 100644 --- a/libs/git-utils/tests/diff.test.ts +++ b/libs/git-utils/tests/diff.test.ts @@ -117,6 +117,18 @@ describe('diff.ts', () => { expect(diff).toContain(`diff --git a/${fileName} b/${fileName}`); expect(diff).toContain('[Unable to read file content]'); }); + + it('should handle directory path gracefully', async () => { + const dirName = 'some-directory'; + const dirPath = path.join(tempDir, dirName); + await fs.mkdir(dirPath); + + const diff = await generateSyntheticDiffForNewFile(tempDir, dirName); + + expect(diff).toContain(`diff --git a/${dirName} b/${dirName}`); + expect(diff).toContain('new file mode 040000'); + expect(diff).toContain('[Directory]'); + }); }); describe('appendUntrackedFileDiffs', () => {