@nerdo/code-reviewer
Version:
A web-based visual git diff tool for reviewing code changes between commits, branches, and tags
133 lines (132 loc) • 5.22 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GitFileRepository = void 0;
const simple_git_1 = __importDefault(require("simple-git"));
const FileChange_1 = require("../../domain/entities/FileChange");
const DiffService_1 = require("../../domain/services/DiffService");
class GitFileRepository {
constructor() {
this.diffService = new DiffService_1.DiffService();
}
getGit(repoPath) {
return (0, simple_git_1.default)(repoPath);
}
async getFileTree(repoPath, commitHash) {
const git = this.getGit(repoPath);
const tree = await git.raw(['ls-tree', '-r', '--name-only', commitHash]);
const files = tree.trim().split('\n').filter(f => f);
return this.buildTreeFromPaths(files);
}
async getFileChanges(repoPath, fromHash, toHash) {
const git = this.getGit(repoPath);
const diffSummary = await git.diffSummary([fromHash, toHash]);
return diffSummary.files.map(file => {
const fileAny = file; // eslint-disable-line @typescript-eslint/no-explicit-any
let changeType;
let previousPath;
if (file.file.includes('=>')) {
const [oldPath, newPath] = file.file.split(' => ').map(p => p.trim());
changeType = oldPath !== newPath ? FileChange_1.FileChangeType.Renamed : FileChange_1.FileChangeType.Modified;
previousPath = oldPath;
}
else if (fileAny.insertions > 0 && fileAny.deletions === 0 && fileAny.changes === fileAny.insertions) {
changeType = FileChange_1.FileChangeType.Added;
}
else if (fileAny.deletions > 0 && fileAny.insertions === 0 && fileAny.changes === fileAny.deletions) {
changeType = FileChange_1.FileChangeType.Deleted;
}
else {
changeType = FileChange_1.FileChangeType.Modified;
}
return {
path: file.file.includes('=>') ? file.file.split(' => ')[1].trim() : file.file,
previousPath,
changeType,
additions: fileAny.insertions || 0,
deletions: fileAny.deletions || 0
};
});
}
async getFileDiff(repoPath, fromHash, toHash, filePath) {
const [oldContent, newContent] = await Promise.all([
this.getFileContent(repoPath, fromHash, filePath).catch(() => ''),
this.getFileContent(repoPath, toHash, filePath).catch(() => '')
]);
// If both contents are the same, it's an unchanged file
if (oldContent === newContent && oldContent !== '') {
return {
path: filePath,
oldContent,
newContent,
hunks: [],
isBinary: false
};
}
const isBinary = await this.isFileBinary(repoPath, toHash, filePath);
const hunks = isBinary ? [] : this.diffService.generateFullFileDiff(oldContent, newContent);
return {
path: filePath,
oldContent,
newContent,
hunks,
isBinary
};
}
async getFileContent(repoPath, commitHash, filePath) {
const git = this.getGit(repoPath);
try {
return await git.show([`${commitHash}:${filePath}`]);
}
catch (error) {
throw new Error(`File ${filePath} not found in commit ${commitHash}`);
}
}
async isFileBinary(repoPath, commitHash, filePath) {
const git = this.getGit(repoPath);
try {
const result = await git.raw(['diff', '--numstat', `${commitHash}~1`, commitHash, '--', filePath]);
return result.includes('-\t-\t');
}
catch {
return false;
}
}
buildTreeFromPaths(paths) {
const root = {
name: '/',
path: '/',
type: 'directory',
children: []
};
for (const filePath of paths) {
const parts = filePath.split('/');
let current = root;
for (let i = 0; i < parts.length; i++) {
const part = parts[i];
const isFile = i === parts.length - 1;
const nodePath = parts.slice(0, i + 1).join('/');
let child = current.children?.find(c => c.name === part);
if (!child) {
child = {
name: part,
path: nodePath,
type: isFile ? 'file' : 'directory',
children: isFile ? undefined : []
};
if (!current.children) {
current.children = [];
}
current.children.push(child);
}
if (!isFile) {
current = child;
}
}
}
return root;
}
}
exports.GitFileRepository = GitFileRepository;