feat: enhance git diff functionality for untracked files

- Implemented synthetic diff generation for untracked files in both git and non-git directories.
- Added fallback UI in the GitDiffPanel for files without diff content, ensuring better user experience.
- Improved error handling and logging for git operations, enhancing reliability in file diff retrieval.

This update allows users to see diffs for new files that are not yet tracked by git, improving the overall functionality of the diff panel.
This commit is contained in:
SuperComboGamer
2025-12-16 00:42:27 -05:00
parent 363be54303
commit 31bb069e75
6 changed files with 413 additions and 19 deletions

View File

@@ -6,9 +6,22 @@ import type { Request, Response } from "express";
import { exec } from "child_process";
import { promisify } from "util";
import { getErrorMessage, logError } from "../common.js";
import { appendUntrackedFileDiffs, generateDiffsForNonGitDirectory } from "../../common.js";
const execAsync = promisify(exec);
/**
* Check if a path is a git repository
*/
async function isGitRepo(repoPath: string): Promise<boolean> {
try {
await execAsync("git rev-parse --is-inside-work-tree", { cwd: repoPath });
return true;
} catch {
return false;
}
}
export function createDiffsHandler() {
return async (req: Request, res: Response): Promise<void> => {
try {
@@ -19,6 +32,26 @@ export function createDiffsHandler() {
return;
}
// Check if it's a git repository
const isRepo = await isGitRepo(projectPath);
if (!isRepo) {
// Not a git repo - list all files and treat them as new
try {
const result = await generateDiffsForNonGitDirectory(projectPath);
res.json({
success: true,
diff: result.diff,
files: result.files,
hasChanges: result.files.length > 0,
});
} catch (error) {
logError(error, "Failed to list files in non-git directory");
res.json({ success: true, diff: "", files: [], hasChanges: false });
}
return;
}
try {
const { stdout: diff } = await execAsync("git diff HEAD", {
cwd: projectPath,
@@ -50,13 +83,19 @@ export function createDiffsHandler() {
};
});
// Generate synthetic diffs for untracked (new) files
// git diff HEAD doesn't include untracked files, so we need to generate them
const combinedDiff = await appendUntrackedFileDiffs(projectPath, diff, files);
res.json({
success: true,
diff,
diff: combinedDiff,
files,
hasChanges: files.length > 0,
});
} catch {
} catch (innerError) {
// Log the error for debugging instead of silently swallowing it
logError(innerError, "Git command failed");
res.json({ success: true, diff: "", files: [], hasChanges: false });
}
} catch (error) {

View File

@@ -6,6 +6,7 @@ import type { Request, Response } from "express";
import { exec } from "child_process";
import { promisify } from "util";
import { getErrorMessage, logError } from "../common.js";
import { generateSyntheticDiffForNewFile } from "../../common.js";
const execAsync = promisify(exec);
@@ -25,16 +26,33 @@ export function createFileDiffHandler() {
}
try {
const { stdout: diff } = await execAsync(
`git diff HEAD -- "${filePath}"`,
{
cwd: projectPath,
maxBuffer: 10 * 1024 * 1024,
}
// First check if the file is untracked
const { stdout: status } = await execAsync(
`git status --porcelain -- "${filePath}"`,
{ cwd: projectPath }
);
const isUntracked = status.trim().startsWith("??");
let diff: string;
if (isUntracked) {
// Generate synthetic diff for untracked file
diff = await generateSyntheticDiffForNewFile(projectPath, filePath);
} else {
// Use regular git diff for tracked files
const result = await execAsync(
`git diff HEAD -- "${filePath}"`,
{
cwd: projectPath,
maxBuffer: 10 * 1024 * 1024,
}
);
diff = result.stdout;
}
res.json({ success: true, diff, filePath });
} catch {
} catch (innerError) {
logError(innerError, "Git file diff failed");
res.json({ success: true, diff: "", filePath });
}
} catch (error) {