using LibGit2Sharp; using Microsoft.Extensions.Logging; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MarketAlly.AIPlugin.Learning { public class GitManager { private readonly string _solutionPath; private readonly string _repositoryPath; private readonly ILogger _logger; private readonly string _correlationId; public GitManager(string solutionPath, ILogger? logger = null) { _solutionPath = solutionPath; _repositoryPath = FindRepositoryRoot(solutionPath); _logger = logger ?? CreateNullLogger(); _correlationId = Guid.NewGuid().ToString("N")[..8]; _logger.LogInformation("GitManager initialized for repository: {RepositoryPath} [CorrelationId: {CorrelationId}]", _repositoryPath, _correlationId); } private static ILogger CreateNullLogger() { using var loggerFactory = LoggerFactory.Create(builder => { }); return loggerFactory.CreateLogger(); } private string FindRepositoryRoot(string startPath) { try { var directory = new DirectoryInfo(Path.GetDirectoryName(startPath)); while (directory != null) { if (Directory.Exists(Path.Combine(directory.FullName, ".git"))) { _logger?.LogDebug("Found Git repository at: {RepositoryPath} [CorrelationId: {CorrelationId}]", directory.FullName, _correlationId); return directory.FullName; } directory = directory.Parent; } _logger?.LogError("Git repository not found starting from: {StartPath} [CorrelationId: {CorrelationId}]", startPath, _correlationId); throw new InvalidOperationException("Not a Git repository or no Git repository found in parent directories"); } catch (Exception ex) { _logger?.LogError(ex, "Error finding Git repository root [CorrelationId: {CorrelationId}]", _correlationId); throw; } } public async Task SetupLearningBranchesAsync(Guid sessionId) { _logger.LogInformation("🌿 Setting up learning branches for session: {SessionId} [CorrelationId: {CorrelationId}]", sessionId, _correlationId); var gitInfo = new GitBranchInfo { SessionId = sessionId, CreatedAt = DateTime.UtcNow }; try { using var repo = new Repository(_repositoryPath); // Check if working directory is clean _logger.LogDebug("Checking working directory status [CorrelationId: {CorrelationId}]", _correlationId); var status = repo.RetrieveStatus(); if (status.IsDirty) { _logger.LogWarning("Working directory is dirty - uncommitted changes detected [CorrelationId: {CorrelationId}]", _correlationId); gitInfo.Success = false; gitInfo.Error = "Working directory has uncommitted changes. Please commit or stash changes before running learning session."; return gitInfo; } // Store original branch gitInfo.OriginalBranch = repo.Head.FriendlyName; gitInfo.OriginalCommit = repo.Head.Tip.Sha; _logger.LogInformation("Current branch: {Branch}, commit: {Commit} [CorrelationId: {CorrelationId}]", gitInfo.OriginalBranch, gitInfo.OriginalCommit[..8], _correlationId); // Ensure we're on master/main var mainBranch = repo.Branches["main"] ?? repo.Branches["master"]; if (mainBranch == null) { _logger.LogError("Could not find main or master branch [CorrelationId: {CorrelationId}]", _correlationId); gitInfo.Error = "Could not find main or master branch"; gitInfo.Success = false; return gitInfo; } Commands.Checkout(repo, mainBranch); // Create or switch to AI branch var aiBranchName = "ai-refactoring"; var aiBranch = repo.Branches[aiBranchName]; if (aiBranch == null) { aiBranch = repo.CreateBranch(aiBranchName); Console.WriteLine($"🌿 Created AI branch: {aiBranchName}"); } Commands.Checkout(repo, aiBranch); gitInfo.AIBranch = aiBranchName; // Create session branch var sessionDate = DateTime.Now.ToString("yyyy-MM-dd"); var sessionBranchName = $"ai-refactoring-{sessionDate}"; // If session branch exists, create unique name if (repo.Branches[sessionBranchName] != null) { var timestamp = DateTime.Now.ToString("HHmmss"); sessionBranchName = $"ai-refactoring-{sessionDate}-{timestamp}"; } var sessionBranch = repo.CreateBranch(sessionBranchName); Commands.Checkout(repo, sessionBranch); gitInfo.SessionBranch = sessionBranchName; // Create failed attempts branch var failedBranchName = $"failed-attempts-{sessionDate}"; if (repo.Branches[failedBranchName] != null) { var timestamp = DateTime.Now.ToString("HHmmss"); failedBranchName = $"failed-attempts-{sessionDate}-{timestamp}"; } var failedBranch = repo.CreateBranch(failedBranchName); gitInfo.FailedAttemptsBranch = failedBranchName; // Switch back to session branch Commands.Checkout(repo, sessionBranch); gitInfo.Success = true; Console.WriteLine($"🌿 Git setup complete:"); Console.WriteLine($" AI Branch: {gitInfo.AIBranch}"); Console.WriteLine($" Session Branch: {gitInfo.SessionBranch}"); Console.WriteLine($" Failed Attempts Branch: {gitInfo.FailedAttemptsBranch}"); return gitInfo; } catch (Exception ex) { gitInfo.Success = false; gitInfo.Error = ex.Message; return gitInfo; } } public async Task CommitSuccessfulIterationAsync(int iterationNumber, string summary) { try { using var repo = new Repository(_repositoryPath); var status = repo.RetrieveStatus(); if (!status.IsDirty) { return false; // Nothing to commit } // Stage all changes Commands.Stage(repo, "*"); // Create commit var signature = new Signature("AI Learning System", "ai@learning.system", DateTimeOffset.Now); var message = $"AI Learning Iteration {iterationNumber}: {summary}"; repo.Commit(message, signature, signature); Console.WriteLine($"✅ Committed iteration {iterationNumber}: {summary}"); return await Task.FromResult(true); } catch (Exception ex) { Console.WriteLine($"❌ Failed to commit iteration {iterationNumber}: {ex.Message}"); return false; } } public async Task CommitFailedAttemptAsync(FailedAttempt attempt) { try { using var repo = new Repository(_repositoryPath); // Switch to failed attempts branch var failedBranch = repo.Branches.FirstOrDefault(b => b.FriendlyName.StartsWith("failed-attempts-")); if (failedBranch != null) { Commands.Checkout(repo, failedBranch); var status = repo.RetrieveStatus(); if (status.IsDirty) { // Stage all changes Commands.Stage(repo, "*"); // Create commit var signature = new Signature("AI Learning System", "ai@learning.system", DateTimeOffset.Now); var message = $"Failed Attempt {attempt.AttemptNumber}: {attempt.FixApproach} on {Path.GetFileName(attempt.FilePath)} - {attempt.Error}"; repo.Commit(message, signature, signature); } // Switch back to session branch var sessionBranch = repo.Branches.FirstOrDefault(b => b.FriendlyName.StartsWith("ai-refactoring-") && b.FriendlyName.Contains(DateTime.Now.ToString("yyyy-MM-dd"))); if (sessionBranch != null) { Commands.Checkout(repo, sessionBranch); } } return await Task.FromResult(true); } catch (Exception ex) { Console.WriteLine($"⚠️ Failed to commit failed attempt: {ex.Message}"); return false; } } public async Task RollbackLastChangeAsync() { try { using var repo = new Repository(_repositoryPath); // Reset to HEAD (undo working directory changes) repo.Reset(ResetMode.Hard); Console.WriteLine("🔄 Rolled back last changes"); return await Task.FromResult(true); } catch (Exception ex) { Console.WriteLine($"❌ Failed to rollback: {ex.Message}"); return false; } } public async Task MergeToAIBranchIfStable() { // This would be called after successful compilation // For now, we'll merge at the end of session instead return await Task.FromResult(true); } public async Task FinalMergeToAIBranchAsync() { try { using var repo = new Repository(_repositoryPath); var aiBranch = repo.Branches["ai-refactoring"]; var sessionBranch = repo.Head; if (aiBranch == null) { return new GitOperationResult { Success = false, Message = "AI branch not found" }; } // Switch to AI branch Commands.Checkout(repo, aiBranch); // Merge session branch var mergeResult = repo.Merge(sessionBranch, new Signature("AI Learning System", "ai@learning.system", DateTimeOffset.Now)); if (mergeResult.Status == MergeStatus.Conflicts) { return new GitOperationResult { Success = false, Message = "Merge conflicts detected - requires manual resolution" }; } return new GitOperationResult { Success = true, Message = $"Successfully merged session to AI branch" }; } catch (Exception ex) { return new GitOperationResult { Success = false, Message = ex.Message }; } } } public class GitBranchInfo { public Guid SessionId { get; set; } public DateTime CreatedAt { get; set; } public bool Success { get; set; } public string Error { get; set; } public string OriginalBranch { get; set; } public string OriginalCommit { get; set; } public string AIBranch { get; set; } public string SessionBranch { get; set; } public string FailedAttemptsBranch { get; set; } public bool FinalMergeSuccess { get; set; } public string FinalMergeMessage { get; set; } } public class GitOperationResult { public bool Success { get; set; } public string Message { get; set; } } }