Spaces:
Sleeping
Sleeping
File size: 4,587 Bytes
a5d1c1b 61b1da1 a5d1c1b 61b1da1 a5d1c1b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | import pc from 'picocolors';
import { createTwoFilesPatch } from 'diff';
import { projectConfigManager } from '../config/project-config.js';
import { cacheService, normalizePosixPath } from '../services/cache-service.js';
import { CliApiClient } from '../client/api-client.js';
import { parseRepoUrl } from '../services/sync-service.js';
import { outputResult, outputError, OutputOptions } from '../utils/output.js';
export interface DiffEntry {
file: string;
type: 'modified' | 'added' | 'deleted';
patch: string;
}
export async function handleDiff(
targetPath?: string,
options: OutputOptions = {}
): Promise<void> {
const workspaceRoot = projectConfigManager.findEvbRoot();
if (!workspaceRoot) {
return outputError(
'Not inside an Ever-Brain project. Run `evb init <repo>` first.',
'NOT_IN_WORKSPACE',
options
);
}
try {
const config = projectConfigManager.readConfig(workspaceRoot);
const state = projectConfigManager.readState(workspaceRoot);
const dirty = cacheService.computeDirtyState(workspaceRoot);
const { owner, repo } = parseRepoUrl(config.source.repository);
const targetBranch = config.source.branch;
const apiUrl = projectConfigManager.getApiUrl(workspaceRoot);
const client = new CliApiClient(apiUrl);
let filesToDiff: Array<{ file: string; type: 'modified' | 'added' | 'deleted' }> = [];
if (targetPath) {
const cleanTarget = normalizePosixPath(targetPath);
if (dirty.modified.includes(cleanTarget)) {
filesToDiff.push({ file: cleanTarget, type: 'modified' });
} else if (dirty.added.includes(cleanTarget)) {
filesToDiff.push({ file: cleanTarget, type: 'added' });
} else if (dirty.deleted.includes(cleanTarget)) {
filesToDiff.push({ file: cleanTarget, type: 'deleted' });
} else {
// Unmodified
outputResult({ diffs: [], message: `File '${cleanTarget}' is clean.` }, options, () => {
console.log(`File '${cleanTarget}' has no uncommitted modifications.`);
});
return;
}
} else {
for (const m of dirty.modified) filesToDiff.push({ file: m, type: 'modified' });
for (const a of dirty.added) filesToDiff.push({ file: a, type: 'added' });
for (const d of dirty.deleted) filesToDiff.push({ file: d, type: 'deleted' });
}
if (filesToDiff.length === 0) {
outputResult({ diffs: [], isDirty: false }, options, () => {
console.log(pc.green('No local modifications (working tree clean).'));
});
return;
}
const diffEntries: DiffEntry[] = [];
for (const item of filesToDiff) {
let oldContent = '';
let newContent = '';
if (item.type === 'modified') {
try {
const remote = await client.getFileContent(owner, repo, item.file, targetBranch);
oldContent = remote.content;
} catch {
oldContent = '';
}
newContent = cacheService.readCachedFile(workspaceRoot, item.file) || '';
} else if (item.type === 'added') {
oldContent = '';
newContent = cacheService.readCachedFile(workspaceRoot, item.file) || '';
} else if (item.type === 'deleted') {
try {
const remote = await client.getFileContent(owner, repo, item.file, targetBranch);
oldContent = remote.content;
} catch {
oldContent = '';
}
newContent = '';
}
const patch = createTwoFilesPatch(
`a/${item.file}`,
`b/${item.file}`,
oldContent,
newContent,
`remote:${targetBranch}`,
'local:cache'
);
diffEntries.push({
file: item.file,
type: item.type,
patch,
});
}
outputResult({ diffs: diffEntries, isDirty: true }, options, () => {
for (const entry of diffEntries) {
console.log(pc.bold(pc.cyan(`\n--- diff: ${entry.file} (${entry.type}) ---`)));
const lines = entry.patch.split('\n');
for (const line of lines) {
if (line.startsWith('+') && !line.startsWith('+++')) {
console.log(pc.green(line));
} else if (line.startsWith('-') && !line.startsWith('---')) {
console.log(pc.red(line));
} else if (line.startsWith('@@')) {
console.log(pc.cyan(line));
} else {
console.log(pc.dim(line));
}
}
}
});
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
outputError(message, 'DIFF_ERROR', options);
}
}
|