Files
ZakirovT 057e1f526b init
2026-01-22 00:45:41 +03:00

231 lines
9.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using LibGit2Sharp;
using PerfReviewSummarizer.Api.Models;
namespace PerfReviewSummarizer.Api.Services;
public class GitService : IGitService
{
public async Task<List<CommitInfo>> GetCommitsAsync(string repositoryPath, DateTime? startDate, DateTime? endDate)
{
return await Task.Run(() =>
{
var commits = new List<CommitInfo>();
if (!Repository.IsValid(repositoryPath))
{
throw new DirectoryNotFoundException($"Неверный путь к репозиторию: {repositoryPath}");
}
using var repo = new Repository(repositoryPath);
var commitFilter = new CommitFilter
{
SortBy = CommitSortStrategies.Time | CommitSortStrategies.Reverse
};
foreach (var commit in repo.Commits.QueryBy(commitFilter))
{
var commitDate = commit.Author.When.DateTime;
if (startDate.HasValue && commitDate < startDate.Value)
continue;
if (endDate.HasValue && commitDate > endDate.Value)
continue;
// Пропускаем merge коммиты
if (IsMergeCommit(commit))
continue;
var parentTree = commit.Parents.FirstOrDefault()?.Tree;
var treeChanges = repo.Diff.Compare<TreeChanges>(parentTree, commit.Tree);
var patch = repo.Diff.Compare<Patch>(parentTree, commit.Tree);
var additions = patch.Sum(p => p.LinesAdded);
var deletions = patch.Sum(p => p.LinesDeleted);
var commitInfo = new CommitInfo
{
Sha = commit.Sha,
Message = commit.MessageShort,
Author = commit.Author.Name,
AuthorEmail = commit.Author.Email,
Committer = commit.Committer.Name,
CommitterEmail = commit.Committer.Email,
When = commit.Author.When,
FilesChanged = treeChanges.Count,
Additions = additions,
Deletions = deletions,
ChangedPaths = treeChanges.Select(t => t.Path).ToList()
};
commits.Add(commitInfo);
}
return commits;
});
}
public async Task<SummaryResponse> GenerateSummaryAsync(string repositoryPath, DateTime? startDate, DateTime? endDate)
{
var commits = await GetCommitsAsync(repositoryPath, startDate, endDate);
if (!commits.Any())
{
return new SummaryResponse
{
Period = new PeriodInfo
{
StartDate = startDate ?? DateTime.MinValue,
EndDate = endDate ?? DateTime.MaxValue,
Description = "Период без коммитов"
}
};
}
var stats = new Statistics
{
TotalCommits = commits.Count,
TotalFilesChanged = commits.Sum(c => c.FilesChanged),
TotalAdditions = commits.Sum(c => c.Additions),
TotalDeletions = commits.Sum(c => c.Deletions),
UniqueContributors = commits.SelectMany(c => new[] { c.Author, c.Committer }).Distinct().Count()
};
var categories = CategorizeCommits(commits);
var topCommits = commits
.OrderByDescending(c => c.FilesChanged + c.Additions + c.Deletions)
.Take(10)
.ToList();
return new SummaryResponse
{
Period = new PeriodInfo
{
StartDate = startDate ?? commits.Min(c => c.When.DateTime),
EndDate = endDate ?? commits.Max(c => c.When.DateTime),
Description = $"С {startDate?.ToString("yyyy-MM-dd") ?? "начала"} по {endDate?.ToString("yyyy-MM-dd") ?? "конца"}"
},
Stats = stats,
Categories = categories,
TopCommits = topCommits
};
}
private List<CategorySummary> CategorizeCommits(List<CommitInfo> commits)
{
var categories = new Dictionary<string, CategorySummary>();
foreach (var commit in commits)
{
var category = DetermineCategory(commit);
if (!categories.ContainsKey(category))
{
categories[category] = new CategorySummary
{
Category = category,
Description = GetCategoryDescription(category),
CommitsCount = 0,
KeyChanges = new List<string>()
};
}
categories[category].CommitsCount++;
if (commit.FilesChanged > 5 || commit.Additions + commit.Deletions > 100)
{
categories[category].KeyChanges.Add(commit.Message);
}
}
return categories.Values
.OrderByDescending(c => c.CommitsCount)
.ToList();
}
private string DetermineCategory(CommitInfo commit)
{
var message = commit.Message.ToLower();
var paths = string.Join(" ", commit.ChangedPaths).ToLower();
// Определение категории по сообщению коммита и измененным путям
if (message.Contains("fix") || message.Contains("bug") || message.Contains("исправ"))
return "Исправления ошибок";
if (message.Contains("feat") || message.Contains("add") || message.Contains("новая") || message.Contains("добав"))
return "Новые функции";
if (message.Contains("refactor") || message.Contains("refactoring") || message.Contains("рефакторинг"))
return "Рефакторинг";
if (message.Contains("test") || message.Contains("тест") || paths.Contains("test"))
return "Тестирование";
if (paths.Contains("api") || paths.Contains("controller") || paths.Contains("endpoint"))
return "API изменения";
if (paths.Contains("ui") || paths.Contains("view") || paths.Contains("component") || paths.Contains("frontend"))
return "UI изменения";
if (paths.Contains("config") || paths.Contains("setting") || message.Contains("config"))
return "Конфигурация";
if (message.Contains("doc") || message.Contains("readme") || message.Contains("документ"))
return "Документация";
if (message.Contains("perf") || message.Contains("optimize") || message.Contains("производительность"))
return "Оптимизация производительности";
if (message.Contains("security") || message.Contains("безопасность"))
return "Безопасность";
return "Прочие изменения";
}
private string GetCategoryDescription(string category)
{
return category switch
{
"Исправления ошибок" => "Работа над исправлением багов и ошибок",
"Новые функции" => "Разработка и добавление новых возможностей",
"Рефакторинг" => "Улучшение структуры кода без изменения функциональности",
"Тестирование" => "Добавление и улучшение тестов",
"API изменения" => "Изменения в API и эндпоинтах",
"UI изменения" => "Изменения в пользовательском интерфейсе",
"Конфигурация" => "Изменения в настройках и конфигурации",
"Документация" => "Обновление документации",
"Оптимизация производительности" => "Улучшение производительности приложения",
"Безопасность" => "Улучшения безопасности",
_ => "Различные изменения в проекте"
};
}
private bool IsMergeCommit(Commit commit)
{
// Merge коммиты имеют больше одного родителя
if (commit.Parents.Count() > 1)
return true;
// Проверяем сообщение коммита на наличие типичных merge паттернов
var message = commit.MessageShort.ToLower();
var fullMessage = commit.Message.ToLower();
var mergePatterns = new[]
{
"merge branch",
"merge pull request",
"merge remote",
"merged ",
"merge from",
"merge into",
"merge:",
"merging",
"merge commit",
"слияние ветки",
"слияние веток",
"мерж ветки"
};
return mergePatterns.Any(pattern => message.Contains(pattern) || fullMessage.Contains(pattern));
}
}