MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Learning/GitManager.cs

319 lines
9.7 KiB
C#
Executable File

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<GitManager> _logger;
private readonly string _correlationId;
public GitManager(string solutionPath, ILogger<GitManager>? 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<GitManager> CreateNullLogger()
{
using var loggerFactory = LoggerFactory.Create(builder => { });
return loggerFactory.CreateLogger<GitManager>();
}
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<GitBranchInfo> 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<bool> 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<bool> 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<bool> 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<bool> 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<GitOperationResult> 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; }
}
}