748 lines
22 KiB
C#
Executable File
748 lines
22 KiB
C#
Executable File
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Hosting;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Diagnostics;
|
|
using System.Text.Json;
|
|
|
|
namespace MarketAlly.AIPlugin.Refactoring.SolutionConsole;
|
|
|
|
class Program
|
|
{
|
|
static async Task<int> Main(string[] args)
|
|
{
|
|
try
|
|
{
|
|
ShowWelcome();
|
|
|
|
// Simple command handling
|
|
if (args.Length > 0)
|
|
{
|
|
await HandleCommand(args);
|
|
return 0;
|
|
}
|
|
|
|
// Interactive mode
|
|
while (true)
|
|
{
|
|
Console.Write("SolutionRefactor> ");
|
|
var input = Console.ReadLine()?.Trim();
|
|
|
|
if (string.IsNullOrEmpty(input)) continue;
|
|
|
|
if (input == "exit" || input == "quit")
|
|
{
|
|
Console.WriteLine("Goodbye!");
|
|
break;
|
|
}
|
|
|
|
if (input == "help")
|
|
{
|
|
ShowHelp();
|
|
continue;
|
|
}
|
|
|
|
var commandArgs = input.Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
|
await HandleCommand(commandArgs);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[ERROR] {ex.Message}");
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static async Task HandleCommand(string[] args)
|
|
{
|
|
var command = args[0].ToLower();
|
|
|
|
try
|
|
{
|
|
switch (command)
|
|
{
|
|
case "scan":
|
|
case "scan-solution":
|
|
var scanPath = GetSolutionPath(args);
|
|
await ScanSolution(scanPath);
|
|
break;
|
|
|
|
case "refactor":
|
|
case "refactor-solution":
|
|
var refactorPath = GetSolutionPath(args);
|
|
var operations = GetOperations(args);
|
|
var apply = args.Contains("--apply");
|
|
await RefactorSolution(refactorPath, operations, apply);
|
|
break;
|
|
|
|
case "git-status":
|
|
var gitPath = GetSolutionPath(args);
|
|
await CheckGitStatus(gitPath);
|
|
break;
|
|
|
|
case "demo":
|
|
await CreateAndDemoSolution();
|
|
break;
|
|
|
|
default:
|
|
Console.WriteLine($"Unknown command: {command}");
|
|
ShowHelp();
|
|
break;
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[ERROR] Command failed: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
static string GetSolutionPath(string[] args)
|
|
{
|
|
var pathIndex = Array.IndexOf(args, "--path");
|
|
if (pathIndex >= 0 && pathIndex + 1 < args.Length)
|
|
{
|
|
return args[pathIndex + 1];
|
|
}
|
|
|
|
// Check current directory for .sln files
|
|
var currentDir = Directory.GetCurrentDirectory();
|
|
if (Directory.GetFiles(currentDir, "*.sln").Any())
|
|
{
|
|
Console.WriteLine($"[INFO] Using solution in current directory: {currentDir}");
|
|
return currentDir;
|
|
}
|
|
|
|
Console.Write("Enter solution directory path: ");
|
|
return Console.ReadLine()?.Trim() ?? currentDir;
|
|
}
|
|
|
|
static string GetOperations(string[] args)
|
|
{
|
|
var opsIndex = Array.IndexOf(args, "--operations");
|
|
if (opsIndex >= 0 && opsIndex + 1 < args.Length)
|
|
{
|
|
return args[opsIndex + 1];
|
|
}
|
|
return "extract-methods,documentation";
|
|
}
|
|
|
|
static async Task ScanSolution(string solutionPath)
|
|
{
|
|
Console.WriteLine($"[SCAN] Analyzing solution: {solutionPath}");
|
|
Console.WriteLine(new string('=', 60));
|
|
|
|
if (!Directory.Exists(solutionPath))
|
|
{
|
|
Console.WriteLine($"[ERROR] Directory not found: {solutionPath}");
|
|
return;
|
|
}
|
|
|
|
// Find solution files
|
|
var solutionFiles = Directory.GetFiles(solutionPath, "*.sln");
|
|
var projectFiles = Directory.GetFiles(solutionPath, "*.csproj", SearchOption.AllDirectories);
|
|
|
|
Console.WriteLine($"[SOLUTION STRUCTURE]");
|
|
Console.WriteLine($" Solution files: {solutionFiles.Length}");
|
|
Console.WriteLine($" Project files: {projectFiles.Length}");
|
|
|
|
// Check .gitignore
|
|
var gitIgnorePath = Path.Combine(solutionPath, ".gitignore");
|
|
Console.WriteLine($" .gitignore: {(File.Exists(gitIgnorePath) ? "✅ Found" : "❌ Missing")}");
|
|
|
|
// Check Git repository
|
|
var isGitRepo = Directory.Exists(Path.Combine(solutionPath, ".git"));
|
|
Console.WriteLine($" Git repository: {(isGitRepo ? "✅ Yes" : "❌ No")}");
|
|
|
|
// Analyze projects
|
|
var totalCsFiles = 0;
|
|
var processableFiles = 0;
|
|
|
|
Console.WriteLine($"\n[PROJECT BREAKDOWN]");
|
|
foreach (var projectFile in projectFiles)
|
|
{
|
|
var projectName = Path.GetFileNameWithoutExtension(projectFile);
|
|
var projectDir = Path.GetDirectoryName(projectFile);
|
|
var csFiles = Directory.GetFiles(projectDir!, "*.cs", SearchOption.AllDirectories);
|
|
|
|
var filteredFiles = csFiles.Where(f => !ShouldExcludeFile(f, solutionPath)).ToList();
|
|
|
|
totalCsFiles += csFiles.Length;
|
|
processableFiles += filteredFiles.Count;
|
|
|
|
var shouldSkip = ShouldSkipProject(projectName);
|
|
var status = shouldSkip ? "⏭️ SKIP" : "✅ PROCESS";
|
|
|
|
Console.WriteLine($" {status} {projectName}: {filteredFiles.Count}/{csFiles.Length} files");
|
|
}
|
|
|
|
Console.WriteLine($"\n[SUMMARY]");
|
|
Console.WriteLine($" Total C# files: {totalCsFiles}");
|
|
Console.WriteLine($" Processable files: {processableFiles}");
|
|
Console.WriteLine($" Would skip: {totalCsFiles - processableFiles} files");
|
|
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
static async Task RefactorSolution(string solutionPath, string operations, bool apply)
|
|
{
|
|
Console.WriteLine($"[REFACTOR] Processing solution: {solutionPath}");
|
|
Console.WriteLine($"[INFO] Operations: {operations}");
|
|
Console.WriteLine($"[INFO] Apply changes: {apply}");
|
|
Console.WriteLine(new string('=', 60));
|
|
|
|
// Simple Git integration
|
|
var gitManager = new SimpleGitManager(solutionPath);
|
|
GitRefactoringInfo? gitInfo = null;
|
|
|
|
if (gitManager.IsGitRepository && apply)
|
|
{
|
|
var branchName = $"refactor/{operations.Split(',')[0]}-{DateTime.Now:yyyyMMdd-HHmm}";
|
|
gitInfo = await gitManager.CreateRefactoringBranch(branchName, true);
|
|
|
|
if (gitInfo.Success)
|
|
{
|
|
Console.WriteLine($"[GIT] Created branch: {gitInfo.NewBranchName}");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"[GIT ERROR] {gitInfo.Error}");
|
|
if (!apply) Console.WriteLine("[INFO] Continuing in preview mode...");
|
|
}
|
|
}
|
|
|
|
// Process projects
|
|
var projectFiles = Directory.GetFiles(solutionPath, "*.csproj", SearchOption.AllDirectories);
|
|
var totalFilesProcessed = 0;
|
|
var totalFilesModified = 0;
|
|
|
|
foreach (var projectFile in projectFiles)
|
|
{
|
|
var projectName = Path.GetFileNameWithoutExtension(projectFile);
|
|
|
|
if (ShouldSkipProject(projectName))
|
|
{
|
|
Console.WriteLine($"[SKIP] {projectName} (test/generated project)");
|
|
continue;
|
|
}
|
|
|
|
Console.WriteLine($"[PROCESSING] {projectName}...");
|
|
|
|
var projectDir = Path.GetDirectoryName(projectFile)!;
|
|
var csFiles = Directory.GetFiles(projectDir, "*.cs", SearchOption.AllDirectories)
|
|
.Where(f => !ShouldExcludeFile(f, solutionPath))
|
|
.ToList();
|
|
|
|
var filesModified = 0;
|
|
foreach (var csFile in csFiles.Take(50)) // Limit for demo
|
|
{
|
|
try
|
|
{
|
|
var modified = await ProcessFile(csFile, operations.Split(','), apply);
|
|
if (modified) filesModified++;
|
|
totalFilesProcessed++;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"[ERROR] Failed to process {Path.GetFileName(csFile)}: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
totalFilesModified += filesModified;
|
|
Console.WriteLine($" ✅ {projectName}: {filesModified}/{csFiles.Count} files modified");
|
|
}
|
|
|
|
Console.WriteLine($"\n[RESULTS]");
|
|
Console.WriteLine($" Files processed: {totalFilesProcessed}");
|
|
Console.WriteLine($" Files modified: {totalFilesModified}");
|
|
Console.WriteLine($" Changes applied: {apply}");
|
|
|
|
// Git commit if changes were applied
|
|
if (apply && gitInfo?.Success == true && totalFilesModified > 0)
|
|
{
|
|
var committed = await gitManager.CommitChanges(
|
|
$"Automated refactoring: {operations}",
|
|
operations.Split(',').ToList()
|
|
);
|
|
|
|
if (committed)
|
|
{
|
|
Console.WriteLine($"\n[GIT] Changes committed to branch: {gitInfo.NewBranchName}");
|
|
ShowGitCommands(gitInfo, operations.Split(',').ToList());
|
|
}
|
|
}
|
|
|
|
await Task.CompletedTask;
|
|
}
|
|
|
|
static async Task<bool> ProcessFile(string filePath, string[] operations, bool apply)
|
|
{
|
|
var content = await File.ReadAllTextAsync(filePath);
|
|
var originalContent = content;
|
|
var modified = false;
|
|
|
|
foreach (var operation in operations)
|
|
{
|
|
switch (operation.Trim().ToLower())
|
|
{
|
|
case "extract-methods":
|
|
content = SimulateMethodExtraction(content);
|
|
modified = true;
|
|
break;
|
|
|
|
case "documentation":
|
|
content = AddSimpleDocumentation(content);
|
|
modified = true;
|
|
break;
|
|
|
|
case "formatting":
|
|
content = SimpleFormatting(content);
|
|
modified = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (modified && apply && content != originalContent)
|
|
{
|
|
// Create backup
|
|
var backupPath = $"{filePath}.{DateTime.Now:yyyyMMdd_HHmmss}.bak";
|
|
File.Copy(filePath, backupPath);
|
|
|
|
// Write modified content
|
|
await File.WriteAllTextAsync(filePath, content);
|
|
return true;
|
|
}
|
|
|
|
return modified && content != originalContent;
|
|
}
|
|
|
|
static string SimulateMethodExtraction(string content)
|
|
{
|
|
// Simple simulation - in reality, this would use Roslyn
|
|
if (content.Contains("// TODO: Extract this method"))
|
|
{
|
|
return content.Replace("// TODO: Extract this method", "// Method extracted automatically");
|
|
}
|
|
|
|
// Add a comment indicating where methods could be extracted
|
|
if (content.Contains("public class") && !content.Contains("// Refactoring opportunity"))
|
|
{
|
|
return content.Replace("public class", "// Refactoring opportunity: Complex methods detected\n public class");
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
static string AddSimpleDocumentation(string content)
|
|
{
|
|
// Simple documentation addition
|
|
var lines = content.Split('\n').ToList();
|
|
var newLines = new List<string>();
|
|
|
|
for (int i = 0; i < lines.Count; i++)
|
|
{
|
|
var line = lines[i];
|
|
|
|
// Add documentation before public methods
|
|
if (line.Trim().StartsWith("public") &&
|
|
(line.Contains(" class ") || line.Contains(" void ") || line.Contains(" string ") || line.Contains(" int ")))
|
|
{
|
|
if (i == 0 || !lines[i - 1].Trim().StartsWith("///"))
|
|
{
|
|
var indent = new string(' ', line.Length - line.TrimStart().Length);
|
|
newLines.Add($"{indent}/// <summary>");
|
|
newLines.Add($"{indent}/// TODO: Add description for this member");
|
|
newLines.Add($"{indent}/// </summary>");
|
|
}
|
|
}
|
|
|
|
newLines.Add(line);
|
|
}
|
|
|
|
return string.Join('\n', newLines);
|
|
}
|
|
|
|
static string SimpleFormatting(string content)
|
|
{
|
|
// Simple formatting improvements
|
|
content = content.Replace("\t", " "); // Convert tabs to spaces
|
|
content = content.Replace("{\r\n\r\n", "{\r\n"); // Remove extra blank lines after {
|
|
content = content.Replace("\n\n\n", "\n\n"); // Reduce multiple blank lines
|
|
return content;
|
|
}
|
|
|
|
static bool ShouldSkipProject(string projectName)
|
|
{
|
|
var skipPatterns = new[] { "test", "tests", "unittest", "integrationtest", "tool", "tools" };
|
|
return skipPatterns.Any(pattern => projectName.ToLower().Contains(pattern));
|
|
}
|
|
|
|
static bool ShouldExcludeFile(string filePath, string basePath)
|
|
{
|
|
var fileName = Path.GetFileName(filePath);
|
|
var relativePath = Path.GetRelativePath(basePath, filePath);
|
|
|
|
// Standard exclusions
|
|
var excludePatterns = new[]
|
|
{
|
|
".Designer.cs", ".generated.cs", ".g.cs", "AssemblyInfo.cs",
|
|
"GlobalAssemblyInfo.cs", "Reference.cs", "TemporaryGeneratedFile"
|
|
};
|
|
|
|
if (excludePatterns.Any(pattern => fileName.Contains(pattern, StringComparison.OrdinalIgnoreCase)))
|
|
return true;
|
|
|
|
// Directory exclusions
|
|
var excludeDirs = new[] { "bin", "obj", ".vs", "packages", "node_modules" };
|
|
if (excludeDirs.Any(dir => relativePath.Contains(dir, StringComparison.OrdinalIgnoreCase)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static async Task CheckGitStatus(string solutionPath)
|
|
{
|
|
Console.WriteLine($"[GIT STATUS] Checking: {solutionPath}");
|
|
Console.WriteLine(new string('=', 60));
|
|
|
|
var gitManager = new SimpleGitManager(solutionPath);
|
|
|
|
if (!gitManager.IsGitRepository)
|
|
{
|
|
Console.WriteLine("[WARNING] Not a Git repository!");
|
|
Console.WriteLine("To initialize Git:");
|
|
Console.WriteLine($" cd \"{solutionPath}\"");
|
|
Console.WriteLine(" git init");
|
|
Console.WriteLine(" git add .");
|
|
Console.WriteLine(" git commit -m \"Initial commit\"");
|
|
return;
|
|
}
|
|
|
|
var status = await gitManager.GetRepositoryStatus();
|
|
|
|
Console.WriteLine($"[REPOSITORY INFO]");
|
|
Console.WriteLine($" Current Branch: {status.CurrentBranch}");
|
|
Console.WriteLine($" Latest Commit: {status.LatestCommitSha[..Math.Min(8, status.LatestCommitSha.Length)]}");
|
|
Console.WriteLine($" Commit Message: {status.LatestCommitMessage}");
|
|
Console.WriteLine($" Author: {status.LatestCommitAuthor}");
|
|
Console.WriteLine($" Is Clean: {(status.IsClean ? "✅ Yes" : "❌ No")}");
|
|
|
|
if (!status.IsClean)
|
|
{
|
|
Console.WriteLine($"\n[UNCOMMITTED CHANGES]");
|
|
Console.WriteLine($"Status output:");
|
|
Console.WriteLine(status.StatusOutput);
|
|
Console.WriteLine($"\n[RECOMMENDATION] Commit or stash changes before refactoring:");
|
|
Console.WriteLine($" git add .");
|
|
Console.WriteLine($" git commit -m \"Work in progress\"");
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine($"\n[READY] ✅ Repository is clean - safe to refactor!");
|
|
}
|
|
}
|
|
|
|
static async Task CreateAndDemoSolution()
|
|
{
|
|
Console.WriteLine("[DEMO] Creating demo solution...");
|
|
|
|
var demoPath = Path.Combine(Directory.GetCurrentDirectory(), "DemoSolution");
|
|
if (Directory.Exists(demoPath))
|
|
{
|
|
Directory.Delete(demoPath, true);
|
|
}
|
|
Directory.CreateDirectory(demoPath);
|
|
|
|
// Create demo solution file
|
|
var solutionContent = @"Microsoft Visual Studio Solution File, Format Version 12.00
|
|
Project(""{9A19103F-16F7-4668-BE54-9A1E7A4F7556}"") = ""DemoApi"", ""DemoApi\DemoApi.csproj"", ""{12345678-1234-1234-1234-123456789012}""
|
|
EndProject";
|
|
await File.WriteAllTextAsync(Path.Combine(demoPath, "DemoSolution.sln"), solutionContent);
|
|
|
|
// Create demo project
|
|
var projectPath = Path.Combine(demoPath, "DemoApi");
|
|
Directory.CreateDirectory(projectPath);
|
|
|
|
var projectContent = @"<Project Sdk=""Microsoft.NET.Sdk"">
|
|
<PropertyGroup>
|
|
<TargetFramework>net8.0</TargetFramework>
|
|
</PropertyGroup>
|
|
</Project>";
|
|
await File.WriteAllTextAsync(Path.Combine(projectPath, "DemoApi.csproj"), projectContent);
|
|
|
|
// Create demo C# file with refactoring opportunities
|
|
var csharpContent = @"using System;
|
|
|
|
namespace DemoApi
|
|
{
|
|
public class OrderService
|
|
{
|
|
// TODO: Extract this method
|
|
public void ProcessOrder(string customerName, string product, decimal price, int quantity)
|
|
{
|
|
if (string.IsNullOrEmpty(customerName))
|
|
{
|
|
throw new ArgumentException(""Customer name required"");
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(product))
|
|
{
|
|
throw new ArgumentException(""Product required"");
|
|
}
|
|
|
|
if (price <= 0)
|
|
{
|
|
throw new ArgumentException(""Price must be positive"");
|
|
}
|
|
|
|
decimal total = price * quantity;
|
|
Console.WriteLine($""Processing order for {customerName}: {quantity}x {product} = ${total}"");
|
|
}
|
|
|
|
public string GetOrderStatus(int orderId)
|
|
{
|
|
return ""Pending"";
|
|
}
|
|
}
|
|
}";
|
|
await File.WriteAllTextAsync(Path.Combine(projectPath, "OrderService.cs"), csharpContent);
|
|
|
|
// Create .gitignore
|
|
var gitIgnoreContent = @"bin/
|
|
obj/
|
|
*.user
|
|
*.suo
|
|
.vs/";
|
|
await File.WriteAllTextAsync(Path.Combine(demoPath, ".gitignore"), gitIgnoreContent);
|
|
|
|
Console.WriteLine($"✅ Demo solution created at: {demoPath}");
|
|
Console.WriteLine("\nNow running demo scan...");
|
|
await ScanSolution(demoPath);
|
|
|
|
Console.WriteLine("\nNow running demo refactoring (preview)...");
|
|
await RefactorSolution(demoPath, "extract-methods,documentation", false);
|
|
|
|
Console.WriteLine($"\n[DEMO COMPLETE] ✅");
|
|
Console.WriteLine($"Demo solution location: {demoPath}");
|
|
Console.WriteLine("Try these commands:");
|
|
Console.WriteLine($" refactor --path \"{demoPath}\" --operations \"extract-methods,documentation\" --apply");
|
|
}
|
|
|
|
static void ShowGitCommands(GitRefactoringInfo gitInfo, List<string> operations)
|
|
{
|
|
Console.WriteLine($"\n[GIT COMMANDS] 🔧");
|
|
Console.WriteLine($"Your refactoring is on branch: {gitInfo.NewBranchName}");
|
|
Console.WriteLine($"Original branch: {gitInfo.OriginalBranch}");
|
|
Console.WriteLine();
|
|
Console.WriteLine("To review changes:");
|
|
Console.WriteLine(" git status");
|
|
Console.WriteLine(" git diff HEAD~1");
|
|
Console.WriteLine();
|
|
Console.WriteLine("To merge to main:");
|
|
Console.WriteLine($" git checkout {gitInfo.OriginalBranch}");
|
|
Console.WriteLine($" git merge {gitInfo.NewBranchName}");
|
|
Console.WriteLine($" git branch -d {gitInfo.NewBranchName}");
|
|
Console.WriteLine();
|
|
Console.WriteLine("To discard changes:");
|
|
Console.WriteLine($" git checkout {gitInfo.OriginalBranch}");
|
|
Console.WriteLine($" git branch -D {gitInfo.NewBranchName}");
|
|
}
|
|
|
|
static void ShowWelcome()
|
|
{
|
|
Console.WriteLine("MarketAlly Solution-Aware Refactoring Tool");
|
|
Console.WriteLine("=" + new string('=', 42));
|
|
Console.WriteLine();
|
|
Console.WriteLine("🎯 Enterprise-ready solution processing");
|
|
Console.WriteLine("✅ Git integration with safe branching");
|
|
Console.WriteLine("✅ Automatic .gitignore respect");
|
|
Console.WriteLine("✅ Test project exclusion");
|
|
Console.WriteLine();
|
|
Console.WriteLine("Type 'help' for commands or 'exit' to quit");
|
|
Console.WriteLine();
|
|
}
|
|
|
|
static void ShowHelp()
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine("Available commands:");
|
|
Console.WriteLine(" scan [--path <dir>] Scan solution structure");
|
|
Console.WriteLine(" refactor [--path <dir>] [--operations <ops>] [--apply] Refactor solution");
|
|
Console.WriteLine(" git-status [--path <dir>] Check Git repository status");
|
|
Console.WriteLine(" demo Create and demo solution");
|
|
Console.WriteLine(" help Show this help");
|
|
Console.WriteLine(" exit Exit application");
|
|
Console.WriteLine();
|
|
Console.WriteLine("Operations: extract-methods, documentation, formatting");
|
|
Console.WriteLine();
|
|
Console.WriteLine("Examples:");
|
|
Console.WriteLine(" scan --path \"C:\\MyProject\"");
|
|
Console.WriteLine(" refactor --path \"C:\\MyProject\" --operations \"extract-methods,documentation\" --apply");
|
|
Console.WriteLine(" demo");
|
|
Console.WriteLine();
|
|
}
|
|
}
|
|
|
|
// SimpleGitManager class (same as previous artifact)
|
|
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<GitRefactoringInfo> CreateRefactoringBranch(string branchName, bool applyChanges)
|
|
{
|
|
var gitInfo = new GitRefactoringInfo
|
|
{
|
|
RepositoryPath = _repositoryPath,
|
|
NewBranchName = branchName,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
if (!IsGitRepository)
|
|
{
|
|
gitInfo.Success = false;
|
|
gitInfo.Error = "Not a Git repository";
|
|
return gitInfo;
|
|
}
|
|
|
|
try
|
|
{
|
|
gitInfo.OriginalBranch = await RunGitCommand("branch --show-current");
|
|
gitInfo.OriginalCommit = await RunGitCommand("rev-parse HEAD");
|
|
|
|
var status = await RunGitCommand("status --porcelain");
|
|
if (!string.IsNullOrWhiteSpace(status))
|
|
{
|
|
gitInfo.Success = false;
|
|
gitInfo.Error = "Working directory has uncommitted changes";
|
|
return gitInfo;
|
|
}
|
|
|
|
if (applyChanges)
|
|
{
|
|
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
|
|
{
|
|
var status = await RunGitCommand("status --porcelain");
|
|
if (string.IsNullOrWhiteSpace(status)) return false;
|
|
|
|
await RunGitCommand("add .");
|
|
await RunGitCommand($"commit -m \"{message}\"");
|
|
return true;
|
|
}
|
|
catch
|
|
{
|
|
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 };
|
|
}
|
|
}
|
|
|
|
private async Task<string> RunGitCommand(string arguments)
|
|
{
|
|
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)
|
|
{
|
|
throw new InvalidOperationException($"Git command failed: {arguments}\nError: {error}");
|
|
}
|
|
|
|
return output.Trim();
|
|
}
|
|
}
|
|
|
|
// Supporting classes
|
|
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;
|
|
}
|
|
|
|
public class GitRefactoringInfo
|
|
{
|
|
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>();
|
|
} |