fix: expand untracked directories to show individual file diffs

Previously, when git status reported an untracked directory (e.g., "?? apps/"),
the code would try to read the directory as a file, which failed and showed
"[Unable to read file content]".

Now, when encountering a directory:
- Strip trailing slash from path (git reports dirs as "dirname/")
- Check if path is a directory using stats.isDirectory()
- Recursively list all files inside using listAllFilesInDirectory
- Generate synthetic diffs for each file found

This ensures users see the actual file contents in the diff view instead
of an error placeholder.
This commit is contained in:
Tony Nekola
2025-12-24 23:16:12 +02:00
parent 8a0226512d
commit 8f2d134d03
2 changed files with 56 additions and 13 deletions

View File

@@ -45,20 +45,24 @@ ${addedLines}
/**
* Generate a synthetic unified diff for an untracked (new) file
* This is needed because `git diff HEAD` doesn't include untracked files
*
* If the path is a directory, this will recursively generate diffs for all files inside
*/
export async function generateSyntheticDiffForNewFile(
basePath: string,
relativePath: string
): Promise<string> {
const fullPath = path.join(basePath, relativePath);
// Remove trailing slash if present (git status reports directories with trailing /)
const cleanPath = relativePath.endsWith('/') ? relativePath.slice(0, -1) : relativePath;
const fullPath = path.join(basePath, cleanPath);
try {
// Check if it's a binary file
if (isBinaryFile(relativePath)) {
return `diff --git a/${relativePath} b/${relativePath}
if (isBinaryFile(cleanPath)) {
return `diff --git a/${cleanPath} b/${cleanPath}
new file mode 100644
index 0000000..0000000
Binary file ${relativePath} added
Binary file ${cleanPath} added
`;
}
@@ -66,15 +70,23 @@ Binary file ${relativePath} added
const stats = await secureFs.stat(fullPath);
// Check if it's a directory (can happen with untracked directories from git status)
// If so, recursively list all files and generate diffs for each
if (stats.isDirectory()) {
return createNewFileDiff(relativePath, '040000', ['[Directory]']);
const filesInDir = await listAllFilesInDirectory(basePath, cleanPath);
if (filesInDir.length === 0) {
// Empty directory
return createNewFileDiff(cleanPath, '040000', ['[Empty directory]']);
}
// Generate diffs for all files in the directory
const diffs = await Promise.all(
filesInDir.map((filePath) => generateSyntheticDiffForNewFile(basePath, filePath))
);
return diffs.join('');
}
if (stats.size > MAX_SYNTHETIC_DIFF_SIZE) {
const sizeKB = Math.round(stats.size / 1024);
return createNewFileDiff(relativePath, '100644', [
`[File too large to display: ${sizeKB}KB]`,
]);
return createNewFileDiff(cleanPath, '100644', [`[File too large to display: ${sizeKB}KB]`]);
}
// Read file content
@@ -91,11 +103,11 @@ Binary file ${relativePath} added
const lineCount = lines.length;
const addedLines = lines.map((line) => `+${line}`).join('\n');
let diff = `diff --git a/${relativePath} b/${relativePath}
let diff = `diff --git a/${cleanPath} b/${cleanPath}
new file mode 100644
index 0000000..0000000
--- /dev/null
+++ b/${relativePath}
+++ b/${cleanPath}
@@ -0,0 +1,${lineCount} @@
${addedLines}`;
@@ -109,7 +121,7 @@ ${addedLines}`;
// Log the error for debugging
logger.error(`Failed to generate synthetic diff for ${fullPath}:`, error);
// Return a placeholder diff
return createNewFileDiff(relativePath, '100644', ['[Unable to read file content]']);
return createNewFileDiff(cleanPath, '100644', ['[Unable to read file content]']);
}
}