From 381d7dc6d8e3acb1fc82888ad9a8db3b731b3dd4 Mon Sep 17 00:00:00 2001 From: Vichea Date: Thu, 12 Sep 2024 19:28:37 -0500 Subject: [PATCH 1/2] Change .gitignore repo --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 4c60693..babe6a4 100644 --- a/.gitignore +++ b/.gitignore @@ -397,4 +397,4 @@ FodyWeavers.xsd # JetBrains Rider *.sln.iml -/SearchBugs.Api/repo/ \ No newline at end of file +src/SearchBugs.Api/repo/ \ No newline at end of file From f8491e85f5ee36bea86ff436f7a43d719441aadd Mon Sep 17 00:00:00 2001 From: Vichea Date: Fri, 13 Sep 2024 10:03:56 -0500 Subject: [PATCH 2/2] Add Git Service & HTTP service --- .../CreateGitRepoCommandHandler.cs | 7 +- .../DeleteGitRepoCommandHandler.cs | 2 +- .../GetGitReposDetailsQueryHandler.cs | 12 +- .../GitHttpServerCommandHandler.cs | 6 +- src/SearchBugs.Domain/Git/Contributor.cs | 9 + src/SearchBugs.Domain/Git/FileBlame.cs | 12 ++ src/SearchBugs.Domain/Git/FileChange.cs | 17 ++ src/SearchBugs.Domain/Git/FileDiff.cs | 10 + src/SearchBugs.Domain/Git/GitErrors.cs | 14 ++ src/SearchBugs.Domain/Git/GitTreeItem.cs | 8 + .../IGitService.cs => Git/IGitHttpService.cs} | 4 +- .../{Repositories => Git}/IGitRepository.cs | 5 +- .../Git/IGitRepositoryService.cs | 5 + src/SearchBugs.Domain/Git/MergeResult.cs | 11 + .../Repositories/GitTreeItem.cs | 30 --- .../Repositories/IGitRepositoryService.cs | 10 - .../Repositories/RepositoryErrors.cs | 0 .../DependencyInjection.cs | 4 +- .../{GitService.cs => GitHttpService.cs} | 7 +- .../Services/GitRepositoryService.cs | 202 +++++++++++++----- .../DependencyInjection.cs | 2 +- .../Repositories/GitRepository.cs | 1 + .../CreateBugCommandHandlerTestData.cs | 26 +-- .../CreateGitRepoCommandHandlerData.cs | 13 ++ .../CreateGitRepoCommandHandlerTest.cs | 27 +++ 25 files changed, 314 insertions(+), 130 deletions(-) create mode 100644 src/SearchBugs.Domain/Git/Contributor.cs create mode 100644 src/SearchBugs.Domain/Git/FileBlame.cs create mode 100644 src/SearchBugs.Domain/Git/FileChange.cs create mode 100644 src/SearchBugs.Domain/Git/FileDiff.cs create mode 100644 src/SearchBugs.Domain/Git/GitErrors.cs create mode 100644 src/SearchBugs.Domain/Git/GitTreeItem.cs rename src/SearchBugs.Domain/{Repositories/IGitService.cs => Git/IGitHttpService.cs} (55%) rename src/SearchBugs.Domain/{Repositories => Git}/IGitRepository.cs (65%) create mode 100644 src/SearchBugs.Domain/Git/IGitRepositoryService.cs create mode 100644 src/SearchBugs.Domain/Git/MergeResult.cs delete mode 100644 src/SearchBugs.Domain/Repositories/GitTreeItem.cs delete mode 100644 src/SearchBugs.Domain/Repositories/IGitRepositoryService.cs create mode 100644 src/SearchBugs.Domain/Repositories/RepositoryErrors.cs rename src/SearchBugs.Infrastructure/Services/{GitService.cs => GitHttpService.cs} (95%) create mode 100644 test/SearchBugs.Application.UnitTests/GitTest/CreateGitRepoCommandHandlerData.cs create mode 100644 test/SearchBugs.Application.UnitTests/GitTest/CreateGitRepoCommandHandlerTest.cs diff --git a/src/SearchBugs.Application/Git/CreateGitRepo/CreateGitRepoCommandHandler.cs b/src/SearchBugs.Application/Git/CreateGitRepo/CreateGitRepoCommandHandler.cs index 1d1c2ae..202ce42 100644 --- a/src/SearchBugs.Application/Git/CreateGitRepo/CreateGitRepoCommandHandler.cs +++ b/src/SearchBugs.Application/Git/CreateGitRepo/CreateGitRepoCommandHandler.cs @@ -1,4 +1,5 @@ using SearchBugs.Domain; +using SearchBugs.Domain.Git; using SearchBugs.Domain.Projects; using SearchBugs.Domain.Repositories; using Shared.Messaging; @@ -8,12 +9,12 @@ namespace SearchBugs.Application.Git.CreateGitRepo; public sealed class CreateGitRepoCommandHandler : ICommandHandler { - private readonly IGitService _gitRepoService; + private readonly IGitHttpService _gitRepoService; private readonly IGitRepository _gitRepository; private readonly IUnitOfWork _unitOfWork; private readonly IProjectRepository _projectRepository; - public CreateGitRepoCommandHandler(IGitService gitService, IGitRepository gitRepository, IUnitOfWork unitOfWork, IProjectRepository projectRepository) + public CreateGitRepoCommandHandler(IGitHttpService gitService, IGitRepository gitRepository, IUnitOfWork unitOfWork, IProjectRepository projectRepository) { _gitRepoService = gitService; _gitRepository = gitRepository; @@ -28,7 +29,7 @@ public async Task Handle(CreateGitRepoCommand request, CancellationToken if (project.IsFailure) return Result.Failure(project.Error); - //_gitRepoService.CreateRepository(repo.Name); + //var repo = await _gitRepoService.CreateRepository(repo.Name); await _gitRepository.Add(repo); await _unitOfWork.SaveChangesAsync(cancellationToken); return Result.Success(); diff --git a/src/SearchBugs.Application/Git/DeleteGitRepo/DeleteGitRepoCommandHandler.cs b/src/SearchBugs.Application/Git/DeleteGitRepo/DeleteGitRepoCommandHandler.cs index 8638526..6d96314 100644 --- a/src/SearchBugs.Application/Git/DeleteGitRepo/DeleteGitRepoCommandHandler.cs +++ b/src/SearchBugs.Application/Git/DeleteGitRepo/DeleteGitRepoCommandHandler.cs @@ -1,5 +1,5 @@ using SearchBugs.Domain; -using SearchBugs.Domain.Repositories; +using SearchBugs.Domain.Git; using Shared.Messaging; using Shared.Results; diff --git a/src/SearchBugs.Application/Git/GetGitReposDetails/GetGitReposDetailsQueryHandler.cs b/src/SearchBugs.Application/Git/GetGitReposDetails/GetGitReposDetailsQueryHandler.cs index 06873cb..ec3a774 100644 --- a/src/SearchBugs.Application/Git/GetGitReposDetails/GetGitReposDetailsQueryHandler.cs +++ b/src/SearchBugs.Application/Git/GetGitReposDetails/GetGitReposDetailsQueryHandler.cs @@ -1,4 +1,5 @@ -using SearchBugs.Infrastructure.Services; +using MediatR; +using SearchBugs.Domain.Git; using Shared.Messaging; using Shared.Results; @@ -13,13 +14,8 @@ public GetGitReposDetailsQueryHandler(IGitRepositoryService gitRepositoryService _gitRepositoryService = gitRepositoryService; } - public Task>> Handle(GetGitReposDetailsQuery request, CancellationToken cancellationToken) + Task>> IRequestHandler>>.Handle(GetGitReposDetailsQuery request, CancellationToken cancellationToken) { - var result = _gitRepositoryService.GetFolderTree(request.RepoName, request.FolderName); - var mappedResult = result.ToDictionary( - x => x.Key, - x => new GitRepoItem(x.Value.Id, x.Value.Url, x.Value.Date, x.Value.ShortMessageHtmlLink) - ); - return Task.FromResult(Result.Success(mappedResult)); + throw new NotImplementedException(); } } diff --git a/src/SearchBugs.Application/Git/GitHttpServer/GitHttpServerCommandHandler.cs b/src/SearchBugs.Application/Git/GitHttpServer/GitHttpServerCommandHandler.cs index b72ccee..0304760 100644 --- a/src/SearchBugs.Application/Git/GitHttpServer/GitHttpServerCommandHandler.cs +++ b/src/SearchBugs.Application/Git/GitHttpServer/GitHttpServerCommandHandler.cs @@ -1,4 +1,4 @@ -using SearchBugs.Domain.Repositories; +using SearchBugs.Domain.Git; using Shared.Messaging; using Shared.Results; @@ -6,9 +6,9 @@ namespace SearchBugs.Application.Git.GitHttpServer; internal sealed class GitHttpServerCommandHandler : ICommandHandler { - private readonly IGitService _gitService; + private readonly IGitHttpService _gitService; - public GitHttpServerCommandHandler(IGitService gitService) => _gitService = gitService; + public GitHttpServerCommandHandler(IGitHttpService gitService) => _gitService = gitService; public async Task Handle(GitHttpServerCommand request, CancellationToken cancellationToken) { diff --git a/src/SearchBugs.Domain/Git/Contributor.cs b/src/SearchBugs.Domain/Git/Contributor.cs new file mode 100644 index 0000000..85b092d --- /dev/null +++ b/src/SearchBugs.Domain/Git/Contributor.cs @@ -0,0 +1,9 @@ +namespace SearchBugs.Infrastructure.Services; + +public record Contributor +{ + public string Name { get; init; } + public string Email { get; init; } + public int CommitCount { get; init; } +} + diff --git a/src/SearchBugs.Domain/Git/FileBlame.cs b/src/SearchBugs.Domain/Git/FileBlame.cs new file mode 100644 index 0000000..b686c05 --- /dev/null +++ b/src/SearchBugs.Domain/Git/FileBlame.cs @@ -0,0 +1,12 @@ +namespace SearchBugs.Infrastructure.Services; + +public record FileBlame +{ + public int LineNumber { get; init; } + public string CommitSha { get; init; } + public string Author { get; init; } + public string Email { get; init; } + public DateTime Date { get; init; } + public int LineContent { get; init; } +} + diff --git a/src/SearchBugs.Domain/Git/FileChange.cs b/src/SearchBugs.Domain/Git/FileChange.cs new file mode 100644 index 0000000..49354ba --- /dev/null +++ b/src/SearchBugs.Domain/Git/FileChange.cs @@ -0,0 +1,17 @@ +namespace SearchBugs.Domain.Git; + +public class FileChange +{ + public string FileName { get; set; } + public string Status { get; set; } + public int Additions { get; set; } + public int Deletions { get; set; } + + public FileChange(string fileName, string status, int additions, int deletions) + { + FileName = fileName; + Status = status; + Additions = additions; + Deletions = deletions; + } +} diff --git a/src/SearchBugs.Domain/Git/FileDiff.cs b/src/SearchBugs.Domain/Git/FileDiff.cs new file mode 100644 index 0000000..b165a1a --- /dev/null +++ b/src/SearchBugs.Domain/Git/FileDiff.cs @@ -0,0 +1,10 @@ +namespace SearchBugs.Infrastructure.Services; + +public record FileDiff +{ + public string FilePath { get; init; } + public string OldPath { get; init; } + public string Status { get; init; } + public string Patch { get; init; } +} + diff --git a/src/SearchBugs.Domain/Git/GitErrors.cs b/src/SearchBugs.Domain/Git/GitErrors.cs new file mode 100644 index 0000000..cd199ed --- /dev/null +++ b/src/SearchBugs.Domain/Git/GitErrors.cs @@ -0,0 +1,14 @@ +using Shared.Errors; + +namespace SearchBugs.Domain.Git; + +public static class GitErrors +{ + public static Error InvalidCommitPath = new Error("Git.InvalidCommitPath", "Invalid path or commit."); + + public static Error FileNotFound = new Error("Git.FileNotFound", "File not found."); + + public static Error BranchNotFound = new Error("Git.BranchNotFound", "Branch not found."); + + public static Error CommitNotFound = new Error("Git.CommitNotFound", "Commit not found."); +} diff --git a/src/SearchBugs.Domain/Git/GitTreeItem.cs b/src/SearchBugs.Domain/Git/GitTreeItem.cs new file mode 100644 index 0000000..6153a1a --- /dev/null +++ b/src/SearchBugs.Domain/Git/GitTreeItem.cs @@ -0,0 +1,8 @@ +namespace SearchBugs.Infrastructure.Services; + +public record GitTreeItem +{ + public string Path { get; init; } + public string Name { get; init; } + public string Type { get; init; } +} diff --git a/src/SearchBugs.Domain/Repositories/IGitService.cs b/src/SearchBugs.Domain/Git/IGitHttpService.cs similarity index 55% rename from src/SearchBugs.Domain/Repositories/IGitService.cs rename to src/SearchBugs.Domain/Git/IGitHttpService.cs index 0486df3..4f9ebfb 100644 --- a/src/SearchBugs.Domain/Repositories/IGitService.cs +++ b/src/SearchBugs.Domain/Git/IGitHttpService.cs @@ -1,6 +1,6 @@ -namespace SearchBugs.Domain.Repositories; +namespace SearchBugs.Domain.Git; -public interface IGitService +public interface IGitHttpService { Task Handle(string repositoryName, CancellationToken cancellationToken = default); } diff --git a/src/SearchBugs.Domain/Repositories/IGitRepository.cs b/src/SearchBugs.Domain/Git/IGitRepository.cs similarity index 65% rename from src/SearchBugs.Domain/Repositories/IGitRepository.cs rename to src/SearchBugs.Domain/Git/IGitRepository.cs index d87d96a..60837d9 100644 --- a/src/SearchBugs.Domain/Repositories/IGitRepository.cs +++ b/src/SearchBugs.Domain/Git/IGitRepository.cs @@ -1,6 +1,7 @@ -using Shared.Results; +using SearchBugs.Domain.Repositories; +using Shared.Results; -namespace SearchBugs.Domain.Repositories; +namespace SearchBugs.Domain.Git; public interface IGitRepository : IRepository { diff --git a/src/SearchBugs.Domain/Git/IGitRepositoryService.cs b/src/SearchBugs.Domain/Git/IGitRepositoryService.cs new file mode 100644 index 0000000..2ae74b4 --- /dev/null +++ b/src/SearchBugs.Domain/Git/IGitRepositoryService.cs @@ -0,0 +1,5 @@ +namespace SearchBugs.Domain.Git; + +public interface IGitRepositoryService +{ +} \ No newline at end of file diff --git a/src/SearchBugs.Domain/Git/MergeResult.cs b/src/SearchBugs.Domain/Git/MergeResult.cs new file mode 100644 index 0000000..ac987d1 --- /dev/null +++ b/src/SearchBugs.Domain/Git/MergeResult.cs @@ -0,0 +1,11 @@ +namespace SearchBugs.Infrastructure.Services; + + +public record MergeResult +{ + public string Status { get; init; } + public string CommitSha { get; init; } +} + + + diff --git a/src/SearchBugs.Domain/Repositories/GitTreeItem.cs b/src/SearchBugs.Domain/Repositories/GitTreeItem.cs deleted file mode 100644 index 06d8382..0000000 --- a/src/SearchBugs.Domain/Repositories/GitTreeItem.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Shared.Primitives; - -namespace SearchBugs.Domain.Repositories; - -public class GitTreeItem : ValueObject -{ - public string Id { get; set; } - public string Url { get; set; } - public DateTime Date { get; set; } - public string ShortMessageHtmlLink { get; set; } - - protected override IEnumerable GetAtomicValues() - { - yield return Id; - yield return Url; - yield return Date; - yield return ShortMessageHtmlLink; - } - - private GitTreeItem(string id, string url, DateTime date, string shortMessageHtmlLink) - { - Id = id; - Url = url; - Date = date; - ShortMessageHtmlLink = shortMessageHtmlLink; - } - - public static GitTreeItem Create(string id, string url, DateTime date, string shortMessageHtmlLink) => - new GitTreeItem(id, url, date, shortMessageHtmlLink); -} diff --git a/src/SearchBugs.Domain/Repositories/IGitRepositoryService.cs b/src/SearchBugs.Domain/Repositories/IGitRepositoryService.cs deleted file mode 100644 index e05f4be..0000000 --- a/src/SearchBugs.Domain/Repositories/IGitRepositoryService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using SearchBugs.Domain.Repositories; - -namespace SearchBugs.Infrastructure.Services; - -public interface IGitRepositoryService -{ - Dictionary GetFolderTree(string repositoryName, string folder); - - GitTreeItem GetRepositoryTree(string repoName); -} \ No newline at end of file diff --git a/src/SearchBugs.Domain/Repositories/RepositoryErrors.cs b/src/SearchBugs.Domain/Repositories/RepositoryErrors.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/SearchBugs.Infrastructure/DependencyInjection.cs b/src/SearchBugs.Infrastructure/DependencyInjection.cs index 29a5cf0..a5d9c26 100644 --- a/src/SearchBugs.Infrastructure/DependencyInjection.cs +++ b/src/SearchBugs.Infrastructure/DependencyInjection.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.DependencyInjection; -using SearchBugs.Domain.Repositories; +using SearchBugs.Domain.Git; using SearchBugs.Domain.Services; using SearchBugs.Domain.Users; using SearchBugs.Infrastructure.Authentication; @@ -33,7 +33,7 @@ public static void AddInfrastructure(this IServiceCollection services) services.ConfigureOptions(); services.AddScoped(); services.AddScoped(); - services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddCors(options => diff --git a/src/SearchBugs.Infrastructure/Services/GitService.cs b/src/SearchBugs.Infrastructure/Services/GitHttpService.cs similarity index 95% rename from src/SearchBugs.Infrastructure/Services/GitService.cs rename to src/SearchBugs.Infrastructure/Services/GitHttpService.cs index 821edce..ca914dc 100644 --- a/src/SearchBugs.Infrastructure/Services/GitService.cs +++ b/src/SearchBugs.Infrastructure/Services/GitHttpService.cs @@ -1,6 +1,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; -using SearchBugs.Domain.Repositories; +using SearchBugs.Domain.Git; using SearchBugs.Infrastructure.Options; using System.Buffers; using System.Diagnostics; @@ -8,13 +8,13 @@ using System.Text; namespace SearchBugs.Infrastructure.Services; -internal class GitService : IGitService +internal class GitHttpService : IGitHttpService { private readonly GitOptions _gitOptions; private readonly IHttpContextAccessor _httpContextAccessor; private HttpContext _httpContext => _httpContextAccessor.HttpContext!; - public GitService(IOptions gitOptions, IHttpContextAccessor httpContextAccessor) + public GitHttpService(IOptions gitOptions, IHttpContextAccessor httpContextAccessor) { _gitOptions = gitOptions.Value; _httpContextAccessor = httpContextAccessor; @@ -102,6 +102,7 @@ private static (SequencePosition Position, bool IsFinished) ReadHeaders( return (reader.Position, false); } + } diff --git a/src/SearchBugs.Infrastructure/Services/GitRepositoryService.cs b/src/SearchBugs.Infrastructure/Services/GitRepositoryService.cs index 1331a27..e4ec694 100644 --- a/src/SearchBugs.Infrastructure/Services/GitRepositoryService.cs +++ b/src/SearchBugs.Infrastructure/Services/GitRepositoryService.cs @@ -1,97 +1,195 @@ using LibGit2Sharp; using Microsoft.Extensions.Options; -using SearchBugs.Domain.Repositories; +using SearchBugs.Domain.Git; using SearchBugs.Infrastructure.Options; +using Shared.Results; namespace SearchBugs.Infrastructure.Services; -internal sealed class GitRepositoryService : IGitRepositoryService +internal sealed partial class GitRepositoryService : IGitRepositoryService { private readonly GitOptions _gitOptions; + private readonly string _basePath; public GitRepositoryService(IOptions gitOptions) { _gitOptions = gitOptions.Value; + _basePath = _gitOptions.BasePath; } - public Dictionary GetFolderTree(string repositoryName, string folder) + public Result> ListTree(string commitSha, string repoPath) { + var _repoPath = Path.Combine(_basePath, repoPath); + using (var repo = new Repository(_basePath)) + { + var commit = repo.Lookup(commitSha) ?? repo.Head.Tip; + var tree = commit.Tree; - var gitPath = Path.Combine(_gitOptions.BasePath, repositoryName); - using var repo = new LibGit2Sharp.Repository(gitPath); + if (tree == null) return Result.Failure>(GitErrors.InvalidCommitPath); - var tree = repo.Head.Tip.Tree; - var treeItems = new Dictionary(); - foreach (var entry in tree) - { - if (entry.TargetType == TreeEntryTargetType.Tree) + return tree.Select(entry => new GitTreeItem { - var date = DateTime.UtcNow; - var shortMessageHtmlLink = "Test"; - var id = entry.Target.Id.Sha; - var url = $"{repositoryName}/{folder}/{entry.Name}"; - treeItems.Add(entry.Name, GitTreeItem.Create(id, url, DateTime.Parse(date.ToString()), shortMessageHtmlLink)); - } + Path = entry.Path, + Name = entry.Name, + Type = entry.TargetType.ToString() + }).ToList(); } + } - return treeItems; + public Result GetFileContent(string commitSha, string filePath) + { + var _repoPath = Path.Combine(_basePath, filePath); + using (var repo = new Repository(_repoPath)) + { + var commit = repo.Lookup(commitSha) ?? repo.Head.Tip; + var blob = commit[filePath]?.Target as Blob; + if (blob == null) return Result.Failure(GitErrors.FileNotFound); + return blob.GetContentText(); + } } - public class TreeItem + public Result CommitChanges(string repoPath, string authorName, string authorEmail, string commitMessage) { + var _repoPath = Path.Combine(_basePath, repoPath); - public string Name { get; set; } - public string Path { get; set; } - public bool IsDirectory { get; set; } - public List Children { get; set; } = new List(); - public TreeItem(string name, string path, bool isDirectory) + using (var repo = new Repository(_repoPath)) { - Name = name; - Path = path; - IsDirectory = isDirectory; + Commands.Stage(repo, "*"); + + var author = new Signature(authorName, authorEmail, DateTimeOffset.Now); + var committer = author; + + // Commit to the repository + repo.Commit(commitMessage, author, committer); } - public void AddChild(TreeItem child) => Children.Add(child); + return Result.Success(); } - public TreeItem GetRepositoryTree(string repoName) + public Result> GetCommitDiff(string repoPath, string commitSha) { - string repoPath = Path.Combine(_gitOptions.BasePath, repoName); - using (var repo = new LibGit2Sharp.Repository(repoPath)) + var _repoPath = Path.Combine(_basePath, repoPath); + using (var repo = new Repository(_repoPath)) { - var rootTree = repo.Head.Tip.Tree; - var rootItem = new TreeItem( - repo.Info.WorkingDirectory, - repo.Info.WorkingDirectory, - true - ); - - PopulateTree(rootTree, rootItem, repo.Info.WorkingDirectory); - return rootItem; + var commit = repo.Lookup(commitSha); + if (commit == null) return Result.Failure>(GitErrors.InvalidCommitPath); + + var diffs = new List(); + + if (commit.Parents.Any()) + { + var parentCommit = commit.Parents.First(); + var patch = repo.Diff.Compare(parentCommit.Tree, commit.Tree); + + diffs.AddRange(patch.Select(p => new FileDiff + { + FilePath = p.Path, + OldPath = p.OldPath, + Status = p.Status.ToString(), + Patch = p.Patch + })); + } + + return diffs; } } - private void PopulateTree(Tree tree, TreeItem parentItem, string parentPath) + public Result> GetFileBlame(string repoPath, string filePath) { - foreach (var entry in tree) + var _repoPath = Path.Combine(_basePath, repoPath); + using (var repo = new Repository(_repoPath)) { - var itemPath = Path.Combine(parentPath, entry.Path); - if (entry.TargetType == TreeEntryTargetType.Tree) + var blame = repo.Blame(filePath); + + return blame.Select(h => new FileBlame { - var treeItem = new TreeItem(entry.Name, itemPath, true); - parentItem.AddChild(treeItem); - PopulateTree((Tree)entry.Target, treeItem, itemPath); - } - else + LineNumber = h.FinalStartLineNumber, + CommitSha = h.FinalCommit.Sha, + Author = h.FinalSignature.Name, + Email = h.FinalSignature.Email, + Date = h.FinalSignature.When.DateTime, + LineContent = h.LineCount, + }).ToList(); + } + } + + public Result MergeBranches(string repoPath, string sourceBranchName, string targetBranchName, string mergerName, string mergerEmail) + { + var _repoPath = Path.Combine(_basePath, repoPath); + using (var repo = new Repository(_repoPath)) + { + var sourceBranch = repo.Branches[sourceBranchName]; + var targetBranch = repo.Branches[targetBranchName]; + + if (sourceBranch == null || targetBranch == null) return Result.Failure(GitErrors.BranchNotFound); + + // Merge + var merger = new Signature(mergerName, mergerEmail, DateTimeOffset.Now); + var result = repo.Merge(sourceBranch, merger); + + return new MergeResult { - parentItem.AddChild(new TreeItem(entry.Name, itemPath, false)); - } + Status = result.Status.ToString(), + CommitSha = result.Commit?.Sha + }; } } - GitTreeItem IGitRepositoryService.GetRepositoryTree(string repoName) + public Result> CompareCommits(string repoPath, string baseCommitSha, string compareCommitSha) { - throw new NotImplementedException(); + var _repoPath = Path.Combine(_basePath, repoPath); + using (var repo = new Repository(_repoPath)) + { + var baseCommit = repo.Lookup(baseCommitSha); + var compareCommit = repo.Lookup(compareCommitSha); + + if (baseCommit == null || compareCommit == null) return Result.Failure>(GitErrors.CommitNotFound); + + var patch = repo.Diff.Compare(baseCommit.Tree, compareCommit.Tree); + + return patch.Select(p => new FileDiff + { + FilePath = p.Path, + OldPath = p.OldPath, + Status = p.Status.ToString(), + Patch = p.Patch + }).ToList(); + } } + + public Result CheckoutBranch(string repoPath, string branchName) + { + var _repoPath = Path.Combine(_basePath, repoPath); + using (var repo = new Repository(_repoPath)) + { + var branch = repo.Branches[branchName]; + if (branch == null) return Result.Failure(GitErrors.BranchNotFound); + + Commands.Checkout(repo, branch); + } + return Result.Success(); + } + + public Result> GetContributors(string repoPath) + { + var _repoPath = Path.Combine(_basePath, repoPath); + using (var repo = new Repository(_repoPath)) + { + var contributors = repo.Commits + .GroupBy(c => new { c.Author.Name, c.Author.Email }) + .Select(group => new Contributor + { + Name = group.Key.Name, + Email = group.Key.Email, + CommitCount = group.Count() + }) + .OrderByDescending(c => c.CommitCount) + .ToList(); + + return contributors; + } + } + } + diff --git a/src/SearchBugs.Persistence/DependencyInjection.cs b/src/SearchBugs.Persistence/DependencyInjection.cs index a003c1b..1951c65 100644 --- a/src/SearchBugs.Persistence/DependencyInjection.cs +++ b/src/SearchBugs.Persistence/DependencyInjection.cs @@ -4,8 +4,8 @@ using Microsoft.Extensions.Options; using SearchBugs.Domain; using SearchBugs.Domain.Bugs; +using SearchBugs.Domain.Git; using SearchBugs.Domain.Projects; -using SearchBugs.Domain.Repositories; using SearchBugs.Domain.Users; using SearchBugs.Persistence.Repositories; using Shared.Data; diff --git a/src/SearchBugs.Persistence/Repositories/GitRepository.cs b/src/SearchBugs.Persistence/Repositories/GitRepository.cs index e1f2c37..f0e366a 100644 --- a/src/SearchBugs.Persistence/Repositories/GitRepository.cs +++ b/src/SearchBugs.Persistence/Repositories/GitRepository.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using SearchBugs.Domain.Git; using SearchBugs.Domain.Repositories; using Shared.Results; diff --git a/test/SearchBugs.Application.UnitTests/BugTrackingTest/CreateBugCommandHandlerTestData.cs b/test/SearchBugs.Application.UnitTests/BugTrackingTest/CreateBugCommandHandlerTestData.cs index bccf56d..f453f32 100644 --- a/test/SearchBugs.Application.UnitTests/BugTrackingTest/CreateBugCommandHandlerTestData.cs +++ b/test/SearchBugs.Application.UnitTests/BugTrackingTest/CreateBugCommandHandlerTestData.cs @@ -14,9 +14,9 @@ public CreateBugCommandHandlerTestData() faker.Lorem.Paragraph(), faker.PickRandom(status), faker.PickRandom(priority), - faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.Random.Guid(), faker.Random.Guid(), - faker.Random.Guid(), faker.Random.Guid())); } @@ -29,9 +29,9 @@ public static IEnumerable InValidBugStatus() faker.Lorem.Paragraph(), faker.PickRandom(status), faker.PickRandom(priority), - faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.Random.Guid(), faker.Random.Guid(), - faker.Random.Guid(), faker.Random.Guid()) }; } @@ -44,9 +44,9 @@ public static IEnumerable InValidBugPriority() faker.Lorem.Paragraph(), faker.PickRandom(status), faker.PickRandom(priority), - faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.Random.Guid(), faker.Random.Guid(), - faker.Random.Guid(), faker.Random.Guid()) }; } @@ -61,7 +61,7 @@ public static IEnumerable InValidBugSeverity() faker.PickRandom(priority), "Invalid", faker.Random.Guid(), - faker.Random.Guid(), + faker.Random.Guid(), faker.Random.Guid()) }; } @@ -74,9 +74,9 @@ public static IEnumerable InValidProjectId() faker.Lorem.Paragraph(), faker.PickRandom(status), faker.PickRandom(priority), - faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), Guid.Empty, - faker.Random.Guid(), + faker.Random.Guid(), faker.Random.Guid()) }; } @@ -89,9 +89,9 @@ public static IEnumerable InValidAssigneeId() faker.Lorem.Paragraph(), faker.PickRandom(status), faker.PickRandom(priority), - faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), faker.Random.Guid(), - Guid.Empty, + Guid.Empty, faker.Random.Guid()) }; } @@ -104,9 +104,9 @@ public static IEnumerable InValidReporterId() faker.Lorem.Paragraph(), faker.PickRandom(status), faker.PickRandom(priority), - faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.PickRandom(BugSeverity.GetValues().Select(x => x.Name).ToArray()), + faker.Random.Guid(), faker.Random.Guid(), - faker.Random.Guid(), Guid.Empty) }; } } diff --git a/test/SearchBugs.Application.UnitTests/GitTest/CreateGitRepoCommandHandlerData.cs b/test/SearchBugs.Application.UnitTests/GitTest/CreateGitRepoCommandHandlerData.cs new file mode 100644 index 0000000..13b07af --- /dev/null +++ b/test/SearchBugs.Application.UnitTests/GitTest/CreateGitRepoCommandHandlerData.cs @@ -0,0 +1,13 @@ +using Bogus; +using SearchBugs.Application.Git.CreateGitRepo; + +namespace SearchBugs.Application.UnitTests.GitTest; + +internal class CreateGitRepoCommandHandlerData : TheoryData +{ + public CreateGitRepoCommandHandlerData() + { + var faker = new Faker(); + Add(new CreateGitRepoCommand(faker.Lorem.Sentence(), faker.Lorem.Paragraph(), faker.Internet.Url(), faker.Random.Guid())); + } +} diff --git a/test/SearchBugs.Application.UnitTests/GitTest/CreateGitRepoCommandHandlerTest.cs b/test/SearchBugs.Application.UnitTests/GitTest/CreateGitRepoCommandHandlerTest.cs new file mode 100644 index 0000000..aa3cc59 --- /dev/null +++ b/test/SearchBugs.Application.UnitTests/GitTest/CreateGitRepoCommandHandlerTest.cs @@ -0,0 +1,27 @@ +using Moq; +using SearchBugs.Application.Git.CreateGitRepo; +using SearchBugs.Domain; +using SearchBugs.Domain.Git; +using SearchBugs.Domain.Projects; + +namespace SearchBugs.Application.UnitTests.GitTest; + +public class CreateGitRepoCommandHandlerTest +{ + private readonly Mock _gitRepository; + private readonly Mock _gitService; + private readonly Mock _projectRepository; + private readonly Mock _unitOfWork; + private readonly CreateGitRepoCommandHandler _sut; + + public CreateGitRepoCommandHandlerTest() + { + _gitRepository = new(); + _gitService = new(); + _projectRepository = new(); + _unitOfWork = new(); + } + + //[Theory] + +}