377 lines
11 KiB
C#
Executable File
377 lines
11 KiB
C#
Executable File
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace MarketAlly.AIPlugin.Refactoring.Plugins
|
|
{
|
|
// Simple Git integration using Process calls - no external dependencies required
|
|
public class SimpleGitManager
|
|
{
|
|
private readonly string _repositoryPath;
|
|
public bool IsGitRepository { get; private set; }
|
|
|
|
public SimpleGitManager(string repositoryPath)
|
|
{
|
|
_repositoryPath = repositoryPath;
|
|
IsGitRepository = Directory.Exists(Path.Combine(repositoryPath, ".git"));
|
|
}
|
|
|
|
public async Task<SimpleGitRefactoringInfo> CreateRefactoringBranch(string branchName, bool applyChanges)
|
|
{
|
|
var gitInfo = new SimpleGitRefactoringInfo
|
|
{
|
|
RepositoryPath = _repositoryPath,
|
|
NewBranchName = branchName,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
if (!IsGitRepository)
|
|
{
|
|
gitInfo.Success = false;
|
|
gitInfo.Error = "Not a Git repository";
|
|
return gitInfo;
|
|
}
|
|
|
|
try
|
|
{
|
|
// Get current branch
|
|
gitInfo.OriginalBranch = await RunGitCommand("branch --show-current");
|
|
|
|
// Get current commit
|
|
gitInfo.OriginalCommit = await RunGitCommand("rev-parse HEAD");
|
|
|
|
// Check if working directory is clean
|
|
var status = await RunGitCommand("status --porcelain");
|
|
if (!string.IsNullOrWhiteSpace(status))
|
|
{
|
|
gitInfo.Success = false;
|
|
gitInfo.Error = "Working directory has uncommitted changes. Please commit or stash changes before refactoring.";
|
|
return gitInfo;
|
|
}
|
|
|
|
if (applyChanges)
|
|
{
|
|
// Check if branch exists
|
|
var branchExists = await RunGitCommand($"show-ref --verify --quiet refs/heads/{branchName}", ignoreErrors: true);
|
|
if (string.IsNullOrEmpty(branchExists)) // Branch doesn't exist (git command failed)
|
|
{
|
|
// Create and checkout new branch
|
|
await RunGitCommand($"checkout -b {branchName}");
|
|
gitInfo.BranchCreated = true;
|
|
}
|
|
else
|
|
{
|
|
// Branch exists, create with timestamp
|
|
var timestamp = DateTime.Now.ToString("HHmmss");
|
|
branchName = $"{branchName}-{timestamp}";
|
|
gitInfo.NewBranchName = branchName;
|
|
await RunGitCommand($"checkout -b {branchName}");
|
|
gitInfo.BranchCreated = true;
|
|
}
|
|
}
|
|
|
|
gitInfo.Success = true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
gitInfo.Success = false;
|
|
gitInfo.Error = ex.Message;
|
|
}
|
|
|
|
return gitInfo;
|
|
}
|
|
|
|
public async Task<bool> CommitChanges(string message, List<string> operationsPerformed)
|
|
{
|
|
if (!IsGitRepository)
|
|
return false;
|
|
|
|
try
|
|
{
|
|
// Check if there are changes to commit
|
|
var status = await RunGitCommand("status --porcelain");
|
|
if (string.IsNullOrWhiteSpace(status))
|
|
{
|
|
Console.WriteLine("No changes to commit.");
|
|
return false;
|
|
}
|
|
|
|
// Stage all changes
|
|
await RunGitCommand("add .");
|
|
|
|
// Create enhanced commit message
|
|
var enhancedMessage = $"{message}\n\nOperations performed:\n{string.Join("\n", operationsPerformed.Select(op => $"- {op}"))}";
|
|
|
|
// Commit changes with secure argument handling
|
|
await RunGitCommandSecure(new[] { "commit", "-m", enhancedMessage });
|
|
|
|
Console.WriteLine($"Changes committed: {message}");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Git commit failed: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task<SimpleGitStatus> GetRepositoryStatus()
|
|
{
|
|
if (!IsGitRepository)
|
|
return new SimpleGitStatus { Error = "Not a Git repository" };
|
|
|
|
try
|
|
{
|
|
var currentBranch = await RunGitCommand("branch --show-current");
|
|
var latestCommit = await RunGitCommand("log -1 --format=\"%H|%s|%an|%ad\" --date=iso");
|
|
var status = await RunGitCommand("status --porcelain");
|
|
|
|
var commitParts = latestCommit.Split('|');
|
|
|
|
return new SimpleGitStatus
|
|
{
|
|
IsClean = string.IsNullOrWhiteSpace(status),
|
|
CurrentBranch = currentBranch.Trim(),
|
|
LatestCommitSha = commitParts.Length > 0 ? commitParts[0].Trim() : "",
|
|
LatestCommitMessage = commitParts.Length > 1 ? commitParts[1].Trim() : "",
|
|
LatestCommitAuthor = commitParts.Length > 2 ? commitParts[2].Trim() : "",
|
|
LatestCommitDate = commitParts.Length > 3 ? commitParts[3].Trim() : "",
|
|
StatusOutput = status
|
|
};
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return new SimpleGitStatus { Error = ex.Message };
|
|
}
|
|
}
|
|
|
|
public async Task<bool> SwitchToBranch(string branchName)
|
|
{
|
|
if (!IsGitRepository)
|
|
return false;
|
|
|
|
try
|
|
{
|
|
await RunGitCommand($"checkout {branchName}");
|
|
Console.WriteLine($"Switched to branch '{branchName}'");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Failed to switch to branch '{branchName}': {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task<bool> DeleteBranch(string branchName, bool force = false)
|
|
{
|
|
if (!IsGitRepository)
|
|
return false;
|
|
|
|
try
|
|
{
|
|
var flag = force ? "-D" : "-d";
|
|
await RunGitCommand($"branch {flag} {branchName}");
|
|
Console.WriteLine($"Deleted branch '{branchName}'");
|
|
return true;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Failed to delete branch '{branchName}': {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public async Task<List<string>> GetBranches()
|
|
{
|
|
if (!IsGitRepository)
|
|
return new List<string>();
|
|
|
|
try
|
|
{
|
|
var output = await RunGitCommand("branch --format='%(refname:short)'");
|
|
return output.Split('\n', StringSplitOptions.RemoveEmptyEntries)
|
|
.Select(b => b.Trim())
|
|
.ToList();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Failed to get branches: {ex.Message}");
|
|
return new List<string>();
|
|
}
|
|
}
|
|
|
|
public async Task<bool> HasUncommittedChanges()
|
|
{
|
|
if (!IsGitRepository)
|
|
return false;
|
|
|
|
try
|
|
{
|
|
var status = await RunGitCommand("status --porcelain");
|
|
return !string.IsNullOrWhiteSpace(status);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Failed to check repository status: {ex.Message}");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private async Task<string> RunGitCommandSecure(string[] arguments, bool ignoreErrors = false)
|
|
{
|
|
var processInfo = new ProcessStartInfo
|
|
{
|
|
FileName = "git",
|
|
WorkingDirectory = _repositoryPath,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true
|
|
};
|
|
|
|
// Use ArgumentList for secure parameter passing
|
|
foreach (var arg in arguments)
|
|
{
|
|
processInfo.ArgumentList.Add(arg);
|
|
}
|
|
|
|
using var process = Process.Start(processInfo);
|
|
if (process == null)
|
|
throw new InvalidOperationException("Failed to start git process");
|
|
|
|
var output = await process.StandardOutput.ReadToEndAsync();
|
|
var error = await process.StandardError.ReadToEndAsync();
|
|
|
|
await process.WaitForExitAsync();
|
|
|
|
if (process.ExitCode != 0 && !ignoreErrors)
|
|
{
|
|
throw new InvalidOperationException($"Git command failed: {error}");
|
|
}
|
|
|
|
return output.Trim();
|
|
}
|
|
|
|
private async Task<string> RunGitCommand(string arguments, bool ignoreErrors = false)
|
|
{
|
|
var processInfo = new ProcessStartInfo
|
|
{
|
|
FileName = "git",
|
|
Arguments = arguments,
|
|
WorkingDirectory = _repositoryPath,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true
|
|
};
|
|
|
|
using var process = Process.Start(processInfo);
|
|
if (process == null)
|
|
throw new InvalidOperationException("Failed to start git process");
|
|
|
|
var output = await process.StandardOutput.ReadToEndAsync();
|
|
var error = await process.StandardError.ReadToEndAsync();
|
|
|
|
await process.WaitForExitAsync();
|
|
|
|
if (process.ExitCode != 0 && !ignoreErrors)
|
|
{
|
|
throw new InvalidOperationException($"Git command failed: {arguments}\nError: {error}");
|
|
}
|
|
|
|
return output.Trim();
|
|
}
|
|
}
|
|
|
|
// Simplified Git status
|
|
public class SimpleGitStatus
|
|
{
|
|
public bool IsClean { get; set; }
|
|
public string CurrentBranch { get; set; } = string.Empty;
|
|
public string LatestCommitSha { get; set; } = string.Empty;
|
|
public string LatestCommitMessage { get; set; } = string.Empty;
|
|
public string LatestCommitAuthor { get; set; } = string.Empty;
|
|
public string LatestCommitDate { get; set; } = string.Empty;
|
|
public string StatusOutput { get; set; } = string.Empty;
|
|
public string Error { get; set; } = string.Empty;
|
|
}
|
|
|
|
// Updated SolutionRefactoringPlugin to use SimpleGitManager instead
|
|
public class SimpleGitRefactoringInfo
|
|
{
|
|
public string RepositoryPath { get; set; } = string.Empty;
|
|
public string OriginalBranch { get; set; } = string.Empty;
|
|
public string OriginalCommit { get; set; } = string.Empty;
|
|
public string NewBranchName { get; set; } = string.Empty;
|
|
public bool BranchCreated { get; set; }
|
|
public bool Success { get; set; }
|
|
public string Error { get; set; } = string.Empty;
|
|
public DateTime CreatedAt { get; set; }
|
|
public List<string> OperationsPerformed { get; set; } = new List<string>();
|
|
}
|
|
|
|
// Simple Git commands generator
|
|
public static class SimpleGitCommands
|
|
{
|
|
public static List<string> GenerateAllCommands(SimpleGitRefactoringInfo gitInfo, List<string> operations)
|
|
{
|
|
var commands = new List<string>();
|
|
|
|
if (gitInfo?.Success == true && gitInfo.BranchCreated)
|
|
{
|
|
commands.Add($"# Refactoring completed on branch: {gitInfo.NewBranchName}");
|
|
commands.Add($"# Original branch: {gitInfo.OriginalBranch}");
|
|
commands.Add("");
|
|
|
|
// Review commands
|
|
commands.Add("# 1. REVIEW CHANGES:");
|
|
commands.Add("git status # See what files changed");
|
|
commands.Add("git diff HEAD~1 # See detailed changes");
|
|
commands.Add("git log --oneline -5 # See recent commits");
|
|
commands.Add("");
|
|
|
|
// Commit commands (if not auto-committed)
|
|
commands.Add("# 2. COMMIT CHANGES (if needed):");
|
|
commands.Add("git add .");
|
|
commands.Add($"git commit -m \"Refactoring: {string.Join(", ", operations)}\"");
|
|
commands.Add("");
|
|
|
|
// Merge commands
|
|
commands.Add("# 3. MERGE TO MAIN (when ready):");
|
|
commands.Add($"git checkout {gitInfo.OriginalBranch}");
|
|
commands.Add($"git merge {gitInfo.NewBranchName}");
|
|
commands.Add($"git branch -d {gitInfo.NewBranchName}");
|
|
commands.Add("");
|
|
|
|
// Rollback commands
|
|
commands.Add("# 4. ROLLBACK (if needed):");
|
|
commands.Add($"git checkout {gitInfo.OriginalBranch}");
|
|
commands.Add($"git branch -D {gitInfo.NewBranchName}");
|
|
commands.Add("# All changes discarded, original code restored!");
|
|
commands.Add("");
|
|
|
|
// Status commands
|
|
commands.Add("# 5. USEFUL COMMANDS:");
|
|
commands.Add("git branch # List all branches");
|
|
commands.Add($"git diff {gitInfo.OriginalBranch}..{gitInfo.NewBranchName} # Compare branches");
|
|
commands.Add("git stash # Temporarily save changes");
|
|
commands.Add("git stash pop # Restore stashed changes");
|
|
}
|
|
else if (gitInfo?.Success == false)
|
|
{
|
|
commands.Add($"# Git operation failed: {gitInfo.Error}");
|
|
commands.Add("# Suggestions:");
|
|
commands.Add("git status # Check repository status");
|
|
commands.Add("git add . # Stage changes");
|
|
commands.Add("git commit -m \"Work in progress\" # Commit current work");
|
|
commands.Add("# Then retry the refactoring operation");
|
|
}
|
|
|
|
return commands;
|
|
}
|
|
}
|
|
} |