mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
Merge pull request #266 from tony-nekola-silk/fix/untracked-directory-diff-display
fix: expand untracked directories to show individual file diffs
This commit is contained in:
@@ -45,36 +45,50 @@ ${addedLines}
|
|||||||
/**
|
/**
|
||||||
* Generate a synthetic unified diff for an untracked (new) file
|
* Generate a synthetic unified diff for an untracked (new) file
|
||||||
* This is needed because `git diff HEAD` doesn't include untracked files
|
* 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(
|
export async function generateSyntheticDiffForNewFile(
|
||||||
basePath: string,
|
basePath: string,
|
||||||
relativePath: string
|
relativePath: string
|
||||||
): Promise<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 {
|
try {
|
||||||
// Check if it's a binary file
|
|
||||||
if (isBinaryFile(relativePath)) {
|
|
||||||
return `diff --git a/${relativePath} b/${relativePath}
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..0000000
|
|
||||||
Binary file ${relativePath} added
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get file stats to check size and type
|
// Get file stats to check size and type
|
||||||
const stats = await secureFs.stat(fullPath);
|
const stats = await secureFs.stat(fullPath);
|
||||||
|
|
||||||
// Check if it's a directory (can happen with untracked directories from git status)
|
// Check if it's a directory first (before binary check)
|
||||||
|
// This handles edge cases like directories named "images.png/"
|
||||||
if (stats.isDirectory()) {
|
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 sequentially
|
||||||
|
// Using sequential processing to avoid exhausting file descriptors on large directories
|
||||||
|
const diffs: string[] = [];
|
||||||
|
for (const filePath of filesInDir) {
|
||||||
|
diffs.push(await generateSyntheticDiffForNewFile(basePath, filePath));
|
||||||
|
}
|
||||||
|
return diffs.join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it's a binary file (after directory check to handle dirs with binary extensions)
|
||||||
|
if (isBinaryFile(cleanPath)) {
|
||||||
|
return `diff --git a/${cleanPath} b/${cleanPath}
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000..0000000
|
||||||
|
Binary file ${cleanPath} added
|
||||||
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stats.size > MAX_SYNTHETIC_DIFF_SIZE) {
|
if (stats.size > MAX_SYNTHETIC_DIFF_SIZE) {
|
||||||
const sizeKB = Math.round(stats.size / 1024);
|
const sizeKB = Math.round(stats.size / 1024);
|
||||||
return createNewFileDiff(relativePath, '100644', [
|
return createNewFileDiff(cleanPath, '100644', [`[File too large to display: ${sizeKB}KB]`]);
|
||||||
`[File too large to display: ${sizeKB}KB]`,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read file content
|
// Read file content
|
||||||
@@ -91,11 +105,11 @@ Binary file ${relativePath} added
|
|||||||
const lineCount = lines.length;
|
const lineCount = lines.length;
|
||||||
const addedLines = lines.map((line) => `+${line}`).join('\n');
|
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
|
new file mode 100644
|
||||||
index 0000000..0000000
|
index 0000000..0000000
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/${relativePath}
|
+++ b/${cleanPath}
|
||||||
@@ -0,0 +1,${lineCount} @@
|
@@ -0,0 +1,${lineCount} @@
|
||||||
${addedLines}`;
|
${addedLines}`;
|
||||||
|
|
||||||
@@ -109,7 +123,7 @@ ${addedLines}`;
|
|||||||
// Log the error for debugging
|
// Log the error for debugging
|
||||||
logger.error(`Failed to generate synthetic diff for ${fullPath}:`, error);
|
logger.error(`Failed to generate synthetic diff for ${fullPath}:`, error);
|
||||||
// Return a placeholder diff
|
// Return a placeholder diff
|
||||||
return createNewFileDiff(relativePath, '100644', ['[Unable to read file content]']);
|
return createNewFileDiff(cleanPath, '100644', ['[Unable to read file content]']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ describe('diff.ts', () => {
|
|||||||
expect(diff).toContain('[Unable to read file content]');
|
expect(diff).toContain('[Unable to read file content]');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle directory path gracefully', async () => {
|
it('should handle empty directory path gracefully', async () => {
|
||||||
const dirName = 'some-directory';
|
const dirName = 'some-directory';
|
||||||
const dirPath = path.join(tempDir, dirName);
|
const dirPath = path.join(tempDir, dirName);
|
||||||
await fs.mkdir(dirPath);
|
await fs.mkdir(dirPath);
|
||||||
@@ -127,7 +127,38 @@ describe('diff.ts', () => {
|
|||||||
|
|
||||||
expect(diff).toContain(`diff --git a/${dirName} b/${dirName}`);
|
expect(diff).toContain(`diff --git a/${dirName} b/${dirName}`);
|
||||||
expect(diff).toContain('new file mode 040000');
|
expect(diff).toContain('new file mode 040000');
|
||||||
expect(diff).toContain('[Directory]');
|
expect(diff).toContain('[Empty directory]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should expand directory with files and generate diffs for each file', async () => {
|
||||||
|
const dirName = 'new-feature';
|
||||||
|
const dirPath = path.join(tempDir, dirName);
|
||||||
|
await fs.mkdir(dirPath);
|
||||||
|
await fs.writeFile(path.join(dirPath, 'index.ts'), 'export const foo = 1;\n');
|
||||||
|
await fs.writeFile(path.join(dirPath, 'utils.ts'), 'export const bar = 2;\n');
|
||||||
|
|
||||||
|
const diff = await generateSyntheticDiffForNewFile(tempDir, dirName);
|
||||||
|
|
||||||
|
// Should contain diffs for both files in the directory
|
||||||
|
expect(diff).toContain(`diff --git a/${dirName}/index.ts b/${dirName}/index.ts`);
|
||||||
|
expect(diff).toContain(`diff --git a/${dirName}/utils.ts b/${dirName}/utils.ts`);
|
||||||
|
expect(diff).toContain('+export const foo = 1;');
|
||||||
|
expect(diff).toContain('+export const bar = 2;');
|
||||||
|
// Should NOT contain a diff for the directory itself
|
||||||
|
expect(diff).not.toContain('[Empty directory]');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle directory path with trailing slash', async () => {
|
||||||
|
const dirName = 'trailing-slash-dir';
|
||||||
|
const dirPath = path.join(tempDir, dirName);
|
||||||
|
await fs.mkdir(dirPath);
|
||||||
|
await fs.writeFile(path.join(dirPath, 'file.txt'), 'content\n');
|
||||||
|
|
||||||
|
// git status reports untracked directories with trailing slash
|
||||||
|
const diff = await generateSyntheticDiffForNewFile(tempDir, `${dirName}/`);
|
||||||
|
|
||||||
|
expect(diff).toContain(`diff --git a/${dirName}/file.txt b/${dirName}/file.txt`);
|
||||||
|
expect(diff).toContain('+content');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user