"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.GitIntegration = void 0; const simple_git_1 = __importDefault(require("simple-git")); const path_1 = __importDefault(require("path")); const fs_extra_1 = __importDefault(require("fs-extra")); class GitIntegration { git; rootDir; constructor(rootDir) { this.rootDir = rootDir || process.cwd(); this.git = (0, simple_git_1.default)(this.rootDir); } async isGitRepo() { try { return await this.git.checkIsRepo(); } catch (e) { return false; } } async getCurrentCommit() { try { const log = await this.git.log({ maxCount: 1 }); return log.latest?.hash; } catch (e) { return undefined; } } async getUnstagedDiff(filePath) { try { const options = filePath ? [filePath] : []; return await this.git.diff(options); } catch (e) { console.error('Error getting unstaged diff:', e); return ''; } } async getStagedDiff(filePath) { try { const options = ['--cached']; if (filePath) options.push(filePath); return await this.git.diff(options); } catch (e) { console.error('Error getting staged diff:', e); return ''; } } /** * Get stats for a specific file based on its current state on disk vs HEAD * This includes both staged and unstaged changes. */ async getFileStats(filePath) { try { // Use git diff --numstat HEAD -- filePath // This compares working directory (and stage) against HEAD const raw = await this.git.raw(['diff', '--numstat', 'HEAD', '--', filePath]); if (!raw.trim()) { // If no output, maybe it's a new untracked file? const status = await this.git.status(); const isUntracked = status.not_added.includes(filePath); if (isUntracked) { // For untracked files, count all lines as added try { const content = await fs_extra_1.default.readFile(path_1.default.resolve(this.rootDir, filePath), 'utf-8'); const lines = content.split('\n').length; return { added: lines, deleted: 0, changeType: 'added' }; } catch (e) { return { added: 0, deleted: 0, changeType: 'modified' }; // Should not happen if file exists } } return { added: 0, deleted: 0, changeType: 'modified' }; } const parts = raw.trim().split(/\s+/); if (parts.length >= 2) { const added = parseInt(parts[0], 10) || 0; const deleted = parseInt(parts[1], 10) || 0; // Determine change type // This is a simplification. 'added' usually means new file, 'deleted' means file removed. // git diff --numstat doesn't explicitly say 'new file'. // We can check git status for more precision if needed. let changeType = 'modified'; // Check status for better type determination const status = await this.git.status(); if (status.created.includes(filePath) || status.not_added.includes(filePath)) { changeType = 'added'; } else if (status.deleted.includes(filePath)) { changeType = 'deleted'; } return { added, deleted, changeType }; } return { added: 0, deleted: 0, changeType: 'modified' }; } catch (e) { console.error(`Error getting file stats for ${filePath}:`, e); return { added: 0, deleted: 0, changeType: 'modified' }; } } /** * Parse a unified diff string to count lines (fallback if numstat isn't enough) */ parseDiff(diff) { let added = 0; let deleted = 0; const lines = diff.split('\n'); for (const line of lines) { if (line.startsWith('+++') || line.startsWith('---')) continue; if (line.startsWith('+')) added++; if (line.startsWith('-')) deleted++; } return { added, deleted }; } /** * Get current branch name */ async getCurrentBranch() { try { const result = await this.git.branch(); return result.current; } catch (e) { return null; } } /** * Create and checkout a new branch */ async createAndCheckoutBranch(branchName) { try { await this.git.checkoutLocalBranch(branchName); return true; } catch (e) { console.error('Error creating branch:', e); return false; } } /** * Checkout an existing branch */ async checkoutBranch(branchName) { try { await this.git.checkout(branchName); return true; } catch (e) { console.error('Error checking out branch:', e); return false; } } /** * Check if branch exists */ async branchExists(branchName) { try { const branches = await this.git.branch(); return branches.all.includes(branchName); } catch (e) { return false; } } /** * Delete a branch */ async deleteBranch(branchName, force = false) { try { await this.git.branch([force ? '-D' : '-d', branchName]); return true; } catch (e) { console.error('Error deleting branch:', e); return false; } } } exports.GitIntegration = GitIntegration;