OPC # 0006: OPC Git Trunk-Based management
This commit is contained in:
@@ -19,13 +19,19 @@ public static class GitEndpoints
|
||||
// All responses include a repoKey field so the caller knows which repo each commit came from.
|
||||
// GET /api/git/log?grep=...&limit=25&page=1&repo=all
|
||||
// page is 1-based. Each repo contributes up to limit*page commits before merge-sort+skip/take.
|
||||
// branch filters to commits reachable from the named local branch; requires a specific repo (not "all").
|
||||
// Each result includes a branches[] array listing local branches that contain that commit.
|
||||
private static IResult GetLog(
|
||||
IConfiguration config,
|
||||
string? grep = null,
|
||||
int limit = 25,
|
||||
int page = 1,
|
||||
string repo = "all")
|
||||
string? grep = null,
|
||||
int limit = 25,
|
||||
int page = 1,
|
||||
string repo = "all",
|
||||
string? branch = null)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(branch) && repo == "all")
|
||||
return Results.BadRequest("'branch' filter requires a specific 'repo' parameter.");
|
||||
|
||||
var repos = repo == "all"
|
||||
? ResolveAllRepos(config)
|
||||
: ResolveNamedRepo(config, repo) is { } p
|
||||
@@ -43,16 +49,30 @@ public static class GitEndpoints
|
||||
|
||||
using var r = new Repository(repoPath);
|
||||
|
||||
var tips = r.Branches
|
||||
.Where(b => b.Tip != null)
|
||||
.Select(b => (GitObject)b.Tip)
|
||||
.ToList();
|
||||
|
||||
var filter = new CommitFilter
|
||||
CommitFilter filter;
|
||||
if (!string.IsNullOrWhiteSpace(branch))
|
||||
{
|
||||
SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time,
|
||||
IncludeReachableFrom = tips.Count > 0 ? tips : (object)r.Head,
|
||||
};
|
||||
var branchRef = r.Branches[branch];
|
||||
if (branchRef?.Tip is null)
|
||||
return Results.BadRequest($"Branch '{branch}' not found in repo '{repoKey}'.");
|
||||
filter = new CommitFilter
|
||||
{
|
||||
SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time,
|
||||
IncludeReachableFrom = branchRef.Tip,
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
var tips = r.Branches
|
||||
.Where(b => b.Tip != null)
|
||||
.Select(b => (GitObject)b.Tip)
|
||||
.ToList();
|
||||
filter = new CommitFilter
|
||||
{
|
||||
SortBy = CommitSortStrategies.Topological | CommitSortStrategies.Time,
|
||||
IncludeReachableFrom = tips.Count > 0 ? tips : (object)r.Head,
|
||||
};
|
||||
}
|
||||
|
||||
IEnumerable<Commit> query = r.Commits.QueryBy(filter);
|
||||
|
||||
@@ -63,22 +83,42 @@ public static class GitEndpoints
|
||||
bucket.Add((c.Author.When, repoKey, ToGitCommit(r, c)));
|
||||
}
|
||||
|
||||
var commits = bucket
|
||||
var pageEntries = bucket
|
||||
.OrderByDescending(x => x.When)
|
||||
.Skip((page - 1) * limit)
|
||||
.Take(limit)
|
||||
.Select(x => new
|
||||
{
|
||||
repoKey = x.RepoKey,
|
||||
hash = x.Commit.Hash,
|
||||
shortHash = x.Commit.ShortHash,
|
||||
author = x.Commit.Author,
|
||||
date = x.Commit.Date,
|
||||
subject = x.Commit.Subject,
|
||||
files = x.Commit.Files,
|
||||
})
|
||||
.ToList();
|
||||
|
||||
// Annotate each commit with the local branches whose tip is a descendant of that commit.
|
||||
var branchMap = new Dictionary<string, List<string>>(StringComparer.Ordinal);
|
||||
foreach (var grp in pageEntries.GroupBy(x => x.RepoKey))
|
||||
{
|
||||
if (!repos.TryGetValue(grp.Key, out var rPath) || !Directory.Exists(rPath)) continue;
|
||||
using var annotRepo = new Repository(rPath);
|
||||
var localBranches = annotRepo.Branches.Where(b => !b.IsRemote && b.Tip != null).ToList();
|
||||
foreach (var (_, _, c) in grp)
|
||||
{
|
||||
var target = annotRepo.Lookup<Commit>(c.Hash);
|
||||
if (target is null) { branchMap[c.Hash] = []; continue; }
|
||||
branchMap[c.Hash] = localBranches
|
||||
.Where(b => annotRepo.ObjectDatabase.FindMergeBase(b.Tip, target)?.Sha == target.Sha)
|
||||
.Select(b => b.FriendlyName)
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
|
||||
var commits = pageEntries.Select(x => new
|
||||
{
|
||||
repoKey = x.RepoKey,
|
||||
hash = x.Commit.Hash,
|
||||
shortHash = x.Commit.ShortHash,
|
||||
author = x.Commit.Author,
|
||||
date = x.Commit.Date,
|
||||
subject = x.Commit.Subject,
|
||||
files = x.Commit.Files,
|
||||
branches = branchMap.TryGetValue(x.Commit.Hash, out var br) ? (IReadOnlyList<string>)br : Array.Empty<string>(),
|
||||
}).ToList();
|
||||
|
||||
return Results.Ok(commits);
|
||||
}
|
||||
|
||||
@@ -127,18 +167,55 @@ public static class GitEndpoints
|
||||
return Results.NotFound();
|
||||
}
|
||||
|
||||
// GET /api/git/branches
|
||||
private static IResult GetBranches(IConfiguration config)
|
||||
// GET /api/git/branches?repo=all
|
||||
// repo defaults to "default" (single configured repo). Pass "all" to get branches from all
|
||||
// registered repos. Each result includes repoKey so the caller can group branches by repo.
|
||||
private static IResult GetBranches(IConfiguration config, string repo = "default")
|
||||
{
|
||||
var repoPath = ResolveRepo(config);
|
||||
if (repo == "all")
|
||||
{
|
||||
var allRepos = ResolveAllRepos(config);
|
||||
if (allRepos.Count == 0)
|
||||
return Results.Problem("Could not locate any git repositories.");
|
||||
|
||||
var allBranches = new List<object>();
|
||||
foreach (var (repoKey, rPath) in allRepos)
|
||||
{
|
||||
if (!Directory.Exists(rPath)) continue;
|
||||
using var gitRepo = new Repository(rPath);
|
||||
allBranches.AddRange(gitRepo.Branches
|
||||
.Where(b => !b.IsRemote && b.Tip != null)
|
||||
.OrderBy(b => b.FriendlyName)
|
||||
.Select(b => (object)new
|
||||
{
|
||||
repoKey,
|
||||
name = b.FriendlyName,
|
||||
hash = b.Tip.Sha,
|
||||
shortHash = b.Tip.Sha[..7],
|
||||
subject = b.Tip.MessageShort,
|
||||
author = b.Tip.Author.Name,
|
||||
date = b.Tip.Author.When.ToString("yyyy-MM-dd HH:mm:ss zzz"),
|
||||
isHead = b.IsCurrentRepositoryHead,
|
||||
}));
|
||||
}
|
||||
return Results.Ok(allBranches);
|
||||
}
|
||||
|
||||
var repoPath = repo == "default"
|
||||
? ResolveRepo(config)
|
||||
: ResolveNamedRepo(config, repo) ?? ResolveRepo(config);
|
||||
|
||||
if (repoPath is null)
|
||||
return Results.Problem("Could not locate a git repository.");
|
||||
|
||||
using var repo = new Repository(repoPath);
|
||||
var branches = repo.Branches
|
||||
var repoLabel = repo == "default" ? "default" : repo;
|
||||
using var singleRepo = new Repository(repoPath);
|
||||
var branches = singleRepo.Branches
|
||||
.Where(b => !b.IsRemote && b.Tip != null)
|
||||
.OrderBy(b => b.FriendlyName)
|
||||
.Select(b => new
|
||||
{
|
||||
repoKey = repoLabel,
|
||||
name = b.FriendlyName,
|
||||
hash = b.Tip.Sha,
|
||||
shortHash = b.Tip.Sha[..7],
|
||||
@@ -147,7 +224,6 @@ public static class GitEndpoints
|
||||
date = b.Tip.Author.When.ToString("yyyy-MM-dd HH:mm:ss zzz"),
|
||||
isHead = b.IsCurrentRepositoryHead,
|
||||
})
|
||||
.OrderBy(b => b.name)
|
||||
.ToList();
|
||||
|
||||
return Results.Ok(branches);
|
||||
|
||||
Reference in New Issue
Block a user