MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Refacto.../Plugins/GitBranchesPlugin.cs

187 lines
7.3 KiB
C#
Executable File

using MarketAlly.AIPlugin;
using LibGit2Sharp;
using System.Text.Json;
namespace MarketAlly.AIPlugin.Refactoring.Plugins;
[AIPlugin("git-branches", "List and analyze branches in a Git repository")]
public class GitBranchesPlugin : IAIPlugin
{
[AIParameter("Local repository path to analyze branches", required: true)]
public string RepositoryPath { get; set; } = string.Empty;
[AIParameter("Include remote branches in the listing", required: false)]
public bool IncludeRemote { get; set; } = true;
[AIParameter("Include detailed branch information (commits, dates)", required: false)]
public bool IncludeDetails { get; set; } = true;
public IReadOnlyDictionary<string, Type> SupportedParameters => new Dictionary<string, Type>
{
["repositoryPath"] = typeof(string),
["repositorypath"] = typeof(string),
["includeRemote"] = typeof(bool),
["includeremote"] = typeof(bool),
["includeDetails"] = typeof(bool),
["includedetails"] = typeof(bool)
};
public async Task<AIPluginResult> ExecuteAsync(IReadOnlyDictionary<string, object> parameters)
{
try
{
// Extract parameters
var repositoryPath = (parameters.ContainsKey("repositoryPath") ? parameters["repositoryPath"] :
parameters.ContainsKey("repositorypath") ? parameters["repositorypath"] : null)?.ToString();
var includeRemote = parameters.ContainsKey("includeRemote") ? Convert.ToBoolean(parameters["includeRemote"]) :
parameters.ContainsKey("includeremote") ? Convert.ToBoolean(parameters["includeremote"]) : true;
var includeDetails = parameters.ContainsKey("includeDetails") ? Convert.ToBoolean(parameters["includeDetails"]) :
parameters.ContainsKey("includedetails") ? Convert.ToBoolean(parameters["includedetails"]) : true;
// Validate repository path
if (string.IsNullOrEmpty(repositoryPath) || !Directory.Exists(repositoryPath))
{
return new AIPluginResult(new ArgumentException("Invalid repository path"), "Repository path does not exist");
}
// Check if it's a Git repository
if (!Directory.Exists(Path.Combine(repositoryPath, ".git")))
{
return new AIPluginResult(new ArgumentException("Not a Git repository"), "Directory is not a Git repository");
}
var result = await GetBranchesAsync(repositoryPath, includeRemote, includeDetails);
return new AIPluginResult(result, "Branches retrieved successfully");
}
catch (Exception ex)
{
// Provide specific error messages based on exception type
if (ex.InnerException is RepositoryNotFoundException || ex is RepositoryNotFoundException)
{
return new AIPluginResult(ex, "Repository not found or corrupted. The directory exists but is not a valid Git repository.");
}
else if (ex.Message.Contains("corrupt") || ex.Message.Contains("invalid"))
{
return new AIPluginResult(ex, "Git repository is corrupted or invalid. Repository reset required.");
}
else if (ex.Message.Contains("access") || ex.Message.Contains("permission"))
{
return new AIPluginResult(ex, "Repository access denied. Check permissions.");
}
else
{
return new AIPluginResult(ex, $"Failed to retrieve branches: {ex.Message}");
}
}
}
private async Task<GitBranchesResult> GetBranchesAsync(string repositoryPath, bool includeRemote, bool includeDetails)
{
return await Task.Run(() =>
{
using var repo = new Repository(repositoryPath);
var result = new GitBranchesResult
{
RepositoryPath = repositoryPath,
CurrentBranch = repo.Head.FriendlyName,
Branches = new List<GitBranchInfo>()
};
// Get local branches
foreach (var branch in repo.Branches.Where(b => !b.IsRemote))
{
var branchInfo = CreateBranchInfo(branch, GitBranchType.Local, includeDetails);
result.Branches.Add(branchInfo);
}
// Get remote branches if requested
if (includeRemote)
{
foreach (var branch in repo.Branches.Where(b => b.IsRemote))
{
var branchInfo = CreateBranchInfo(branch, GitBranchType.Remote, includeDetails);
result.Branches.Add(branchInfo);
}
}
// Set default branch info
var defaultBranch = result.Branches.FirstOrDefault(b => b.IsDefault);
if (defaultBranch != null)
{
result.DefaultBranch = defaultBranch.Name;
}
result.TotalBranches = result.Branches.Count;
result.LocalBranches = result.Branches.Count(b => b.Type == GitBranchType.Local);
result.RemoteBranches = result.Branches.Count(b => b.Type == GitBranchType.Remote);
return result;
});
}
private GitBranchInfo CreateBranchInfo(Branch branch, GitBranchType type, bool includeDetails)
{
var branchInfo = new GitBranchInfo
{
Name = branch.FriendlyName,
Type = type,
IsDefault = IsDefaultBranch(branch.FriendlyName),
IsCurrent = branch.IsCurrentRepositoryHead,
IsActive = !branch.IsRemote && branch.Tip != null
};
if (includeDetails && branch.Tip != null)
{
branchInfo.LastCommitHash = branch.Tip.Sha;
branchInfo.LastCommitMessage = branch.Tip.MessageShort;
branchInfo.LastCommitAuthor = branch.Tip.Author.Name;
branchInfo.LastCommitDate = branch.Tip.Author.When.DateTime;
branchInfo.CommitsAhead = 0; // Would need complex calculation
branchInfo.CommitsBehind = 0; // Would need complex calculation
}
return branchInfo;
}
private bool IsDefaultBranch(string branchName)
{
var defaultBranches = new[] { "main", "master", "develop", "development" };
return defaultBranches.Contains(branchName.ToLower());
}
}
// Data models for Git branches
public class GitBranchesResult
{
public string RepositoryPath { get; set; } = string.Empty;
public string CurrentBranch { get; set; } = string.Empty;
public string? DefaultBranch { get; set; }
public int TotalBranches { get; set; }
public int LocalBranches { get; set; }
public int RemoteBranches { get; set; }
public List<GitBranchInfo> Branches { get; set; } = new();
}
public class GitBranchInfo
{
public string Name { get; set; } = string.Empty;
public GitBranchType Type { get; set; }
public bool IsDefault { get; set; }
public bool IsCurrent { get; set; }
public bool IsActive { get; set; }
public string? LastCommitHash { get; set; }
public string? LastCommitMessage { get; set; }
public string? LastCommitAuthor { get; set; }
public DateTime? LastCommitDate { get; set; }
public int CommitsAhead { get; set; }
public int CommitsBehind { get; set; }
}
public enum GitBranchType
{
Local,
Remote
}