gitlify
Version:
A powerful CLI tool to analyze uncommitted git changes with detailed reports, function detection, and beautiful terminal output
136 lines (117 loc) • 3.83 kB
JavaScript
const { exec } = require('child_process');
const { promisify } = require('util');
const execAsync = promisify(exec);
const InputValidator = require('./utils/validator.js');
const { GitError, SecurityError, ErrorHandler } = require('./utils/error-handler.js');
const config = require('./config.js');
class GitAnalyzer {
constructor() {
this.cwd = process.cwd();
}
async isGitRepository() {
try {
await execAsync('git rev-parse --git-dir', { cwd: this.cwd });
return true;
} catch (error) {
return false;
}
}
async getStatus() {
try {
const gitOptions = config.getGitOptions();
const { stdout } = await execAsync('git status --porcelain', {
cwd: this.cwd,
...gitOptions
});
const lines = stdout.trim().split('\n').filter(line => line.length > 0);
// Validate file paths
const validatedFiles = lines.map(line => {
const status = line.substring(0, 2).trim();
const filePath = line.substring(3);
// Validate file path
try {
InputValidator.validateFilePath(filePath);
} catch (error) {
throw new SecurityError(`Invalid file path in git status: ${filePath}`, {
originalError: error.message
});
}
return { status, filePath };
});
return {
hasChanges: validatedFiles.length > 0,
files: validatedFiles
};
} catch (error) {
throw new GitError('Failed to get git status', {
originalError: error.message,
command: 'git status --porcelain'
});
}
}
async getDiff() {
try {
const gitOptions = config.getGitOptions();
// Get diff for staged changes
const { stdout: stagedDiff } = await execAsync('git diff --cached', {
cwd: this.cwd,
...gitOptions
});
// Get diff for unstaged changes
const { stdout: unstagedDiff } = await execAsync('git diff', {
cwd: this.cwd,
...gitOptions
});
return {
staged: stagedDiff,
unstaged: unstagedDiff,
combined: stagedDiff + '\n' + unstagedDiff
};
} catch (error) {
throw new GitError('Failed to get git diff', {
originalError: error.message,
command: 'git diff'
});
}
}
async getFileContent(filePath) {
try {
// Input validation
InputValidator.validateFilePath(filePath);
const gitOptions = config.getGitOptions();
const { stdout } = await execAsync(`git show HEAD:${filePath}`, {
cwd: this.cwd,
...gitOptions
});
return stdout;
} catch (error) {
// File might not exist in HEAD, return empty content
if (error.code === 'ENOENT' || error.message.includes('does not exist')) {
return '';
}
throw new GitError('Failed to get file content from git', {
originalError: error.message,
filePath: filePath
});
}
}
async getCurrentFileContent(filePath) {
try {
const fs = require('fs');
// Path validation and resolution
const fullPath = InputValidator.validateAndResolvePath(filePath, this.cwd);
// File size validation
InputValidator.validateFileSize(fullPath, config.maxFileSize);
return fs.readFileSync(fullPath, 'utf8');
} catch (error) {
if (error.code === 'ENOENT') {
return '';
}
throw new FileSystemError('Failed to read current file content', {
originalError: error.message,
filePath: filePath
});
}
}
}
module.exports = GitAnalyzer;