feat: version diffs work now

This commit is contained in:
2026-02-16 14:03:09 +01:00
parent 3c9d4b6bce
commit b19e92f729
13 changed files with 655 additions and 20 deletions

View File

@@ -47,6 +47,19 @@ export interface GitDiffContentDto {
modified: string;
}
export interface GitCommitDiffContentDto {
commitHash: string;
original: string;
modified: string;
files: GitCommitDiffFileDto[];
}
export interface GitCommitDiffFileDto {
filePath: string;
original: string;
modified: string;
}
export interface GitHistoryEntry {
hash: string;
shortHash: string;
@@ -505,6 +518,129 @@ export class GitEngine {
};
}
async getCommitDiffContent(projectPath: string, commitHash: string): Promise<GitCommitDiffContentDto> {
const git = simpleGit(projectPath);
const patch = await git.show(['--format=', '--patch', commitHash]);
const files = this.parseUnifiedPatchFiles(patch);
if (files.length === 0) {
return {
commitHash,
original: '',
modified: patch,
files: [],
};
}
const firstFile = files[0];
return {
commitHash,
original: firstFile.original,
modified: firstFile.modified,
files,
};
}
private parseUnifiedPatchFiles(patch: string): GitCommitDiffFileDto[] {
interface FileDiffBuffers {
path: string;
original: string[];
modified: string[];
inHunk: boolean;
touched: boolean;
}
const lines = patch.split('\n');
const files: FileDiffBuffers[] = [];
let currentFile: FileDiffBuffers | null = null;
const flushCurrent = () => {
if (!currentFile) {
return;
}
if (currentFile.touched || currentFile.original.length > 0 || currentFile.modified.length > 0) {
files.push(currentFile);
}
currentFile = null;
};
for (const line of lines) {
if (line.startsWith('diff --git ')) {
flushCurrent();
const match = line.match(/^diff --git a\/(.+) b\/(.+)$/);
const filePath = match ? match[2] : line;
currentFile = {
path: filePath,
original: [],
modified: [],
inHunk: false,
touched: false,
};
continue;
}
if (!currentFile) {
continue;
}
if (line.startsWith('@@')) {
currentFile.inHunk = true;
continue;
}
if (line.startsWith('Binary files ')) {
currentFile.original.push(line);
currentFile.modified.push(line);
currentFile.touched = true;
continue;
}
if (!currentFile.inHunk) {
continue;
}
if (line.startsWith('\\ No newline at end of file')) {
continue;
}
if (line.startsWith('+')) {
currentFile.modified.push(line.slice(1));
currentFile.touched = true;
continue;
}
if (line.startsWith('-')) {
currentFile.original.push(line.slice(1));
currentFile.touched = true;
continue;
}
if (line.startsWith(' ')) {
const contextLine = line.slice(1);
currentFile.original.push(contextLine);
currentFile.modified.push(contextLine);
currentFile.touched = true;
continue;
}
currentFile.original.push(line);
currentFile.modified.push(line);
currentFile.touched = true;
}
flushCurrent();
return files.map((file) => ({
filePath: file.path,
original: file.original.join('\n'),
modified: file.modified.join('\n'),
}));
}
async getHistory(projectPath: string, limit = 20): Promise<GitHistoryEntry[]> {
const git = simpleGit(projectPath);
const history = await git.log({ maxCount: limit });

View File

@@ -58,6 +58,11 @@ export function registerIpcHandlers(): void {
return engine.getDiffContent(projectPath, filePath);
});
safeHandle('git:commitDiffContent', async (_, projectPath: string, commitHash: string) => {
const engine = getGitEngine();
return engine.getCommitDiffContent(projectPath, commitHash);
});
safeHandle('git:history', async (_, projectPath: string, limit?: number) => {
const engine = getGitEngine();
return engine.getHistory(projectPath, limit);

View File

@@ -12,6 +12,7 @@ export const electronAPI: ElectronAPI = {
getStatus: (projectPath: string) => ipcRenderer.invoke('git:status', projectPath),
getDiff: (projectPath: string, filePath: string) => ipcRenderer.invoke('git:diff', projectPath, filePath),
getDiffContent: (projectPath: string, filePath: string) => ipcRenderer.invoke('git:diffContent', projectPath, filePath),
getCommitDiffContent: (projectPath: string, commitHash: string) => ipcRenderer.invoke('git:commitDiffContent', projectPath, commitHash),
getHistory: (projectPath: string, limit?: number) => ipcRenderer.invoke('git:history', projectPath, limit),
fetch: (projectPath: string) => ipcRenderer.invoke('git:fetch', projectPath),
pull: (projectPath: string) => ipcRenderer.invoke('git:pull', projectPath),

View File

@@ -247,6 +247,19 @@ export interface GitDiffContentDto {
modified: string;
}
export interface GitCommitDiffContentDto {
commitHash: string;
original: string;
modified: string;
files: GitCommitDiffFileDto[];
}
export interface GitCommitDiffFileDto {
filePath: string;
original: string;
modified: string;
}
export interface GitHistoryEntry {
hash: string;
shortHash: string;
@@ -376,6 +389,7 @@ export interface ElectronAPI {
getStatus: (projectPath: string) => Promise<GitStatusDto>;
getDiff: (projectPath: string, filePath: string) => Promise<GitDiffDto>;
getDiffContent: (projectPath: string, filePath: string) => Promise<GitDiffContentDto>;
getCommitDiffContent: (projectPath: string, commitHash: string) => Promise<GitCommitDiffContentDto>;
getHistory: (projectPath: string, limit?: number) => Promise<GitHistoryEntry[]>;
fetch: (projectPath: string) => Promise<GitActionResult>;
pull: (projectPath: string) => Promise<GitActionResult>;