我正在尝试为GitHub的GraphQL v4 GraphQL API构造一个查询,以从给定的存储库(不管分支)检索所有提交。
以github/training-kit存储库为例,我目前必须分几个步骤这样做,即:
pageInfo重复查询以获取所有分支):{
repository(owner: "github", name: "training-kit") {
refs(first: 10, refPrefix: "refs/heads/", after: "") {
totalCount
edges {
node {
name
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}master分支的查询,以获取该分支的第一个100个提交:{
repository(owner: "github", name: "training-kit") {
refs(query: "master", refPrefix: "refs/heads/", first: 1) {
nodes {
target {
... on Commit {
history(first: 100) {
nodes {
oid
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
}
}
}对我来说,这个解决方案效率低下,特别是因为第2步。在第2步中,大多数提交将在多个分支中重复(更不用说,为了从一个分支获得所有提交,我必须进行许多查询)。一旦我从每个分支得到提交的列表,我就必须去复制它们。整个过程需要很多很多查询和大量重复的工作。但是,由于有些提交只能由某些分支来完成,所以除了穷尽地查询每个分支之外,我看不出还有什么方法可以做到。
是否可以提出更有效的策略,更好地利用GitHub GraphQL 从存储库检索 all ?E 222
谢谢!
P.S.作为参考,我看过以下问题,但似乎没有回答我的问题:
a. Github GraphQL - Getting a repository's list of commits --他们的目标仅仅是从存储库的默认分支获取最新的n提交数,而不是不管哪个分支都提交。
b. Commits stats from github using graphql --这个问题只对默认分支感兴趣,它可能不包括所有提交。
c. Querying all commits in a single repository with the GitHub GraphQL API v4 -只对master分支和如何进行分页感兴趣,而不是对存储库的所有提交都感兴趣。
发布于 2022-01-19 20:08:32
这是我基于C#的关于如何解决这个问题的想法,也许不是通过完全解决它,而是提高性能。下面的代码解决了“在存储库的默认分支中检索所有提交”的问题,但是,它可以应用于GitHub GraphQL上几乎任何基于游标的分页场景。我知道你的问题是关于“所有分支机构的所有承诺,重复”,然而,我相信这种方法也可能对你有用。
查询大型存储库的固有问题是每个页面的100个结果,以及必须逐个迭代页面的事实,因为每个页面都包含指向下一页的游标。我已经解决了解决方案中的游标标识问题,并同时发送了所有页面请求,从而缩短了整个执行时间。
其想法是创建对GitHub GraphQL API的初始请求,只获取给定过滤器的总计数。我以为我们每页会得到100个结果。由于GitHub提交页游标总是采用"xX9XXXXXXX3961722145Xf39cc9617XXXXxxx 99“格式,其中第一部分是第一次提交in (第一页的第一次提交--所有页面上的所有游标都使用此in-迭代时它不会更改),而99是上一页最后提交的序号(基于0的索引),因此很容易计算670次提交的每个存储库的游标是什么,只需发出"totalCount”请求:
生成标识每个页面开头的游标之后,我们可以为每个页面准备一个单独的Task,其中Task将包含对GitHub GraphQL的请求,以获取一个页面,并使用Task.WhenAll来执行所有这些请求。
我已经在一个670次提交的存储库上测试了这一点,所有的7个页面都是在大约7秒的时间内获得的。如果我遍历每一页,每页大约需要4秒,总计25到30秒。
应该注意的是,这没有在生产环境中进行测试,它不涉及错误处理,而且并行/并发实现很可能会得到改进,因此它只应被视为概念的证明。此外,我不确定当您发送包含100页或1000页提交的存储库的请求时,GitHub API将如何处理。
public async Task<List<Commit>> GetCommitsByPeriodAsync(Guid integrationId, DateTime since, string repositoryName, string repositoryOwner)
{
string initialCursor = null;
var firstPageInfo = await GetDefaultBranchCommitsFirstPageInfoAsync(since, initialCursor, repositoryOwner, repositoryName);
var commitPagesCursors = GetCommitPagesCursors(firstPageInfo, initialCursor );
var tasks = commitPagesCursors.Select(x => GetDefaultBranchCommitsPageByPeriodAsync(since, x, repositoryOwner, repositoryName));
var results = await Task.WhenAll(tasks);
var branchCommitsByPeriod = results.SelectMany(x => x.Commits)
.ToList();
return branchCommitsByPeriod;
}
private List<string> GetCommitPagesCursors(GetCommitsPageInfoResponse firstPageInfo, string initialCursor)
{
// Two initial cursors will always be "null", and "oid 99" for 100 items pages
var cursors = new List<string> { initialCursor, firstPageInfo.PageInfo.EndCursor };
int totalCount = firstPageInfo.TotalCount;
var firstCommitCursorSplit = firstPageInfo.PageInfo.EndCursor.Split(" ");
var firstCommitId = firstCommitCursorSplit[0];
var lastPageCommitNumberString = firstCommitCursorSplit[1];
// TO DO: handling TryParse failure scenario
int.TryParse(lastPageCommitNumberString, out int lastPageCommitNumber);
// 100 is the max number of objects in a page
lastPageCommitNumber += 100;
while (lastPageCommitNumber < totalCount)
{
string nextPageCursor = $"{firstCommitId} {lastPageCommitNumber}";
cursors.Add(nextPageCursor);
lastPageCommitNumber += 100;
}
return cursors;
}
public async Task<GetCommitsPageInfoResponse> GetDefaultBranchCommitsFirstPageInfoAsync(DateTime since, string cursor, string repositoryOwner, string repositoryName)
{
// Code omitted for brevity
var commitsRequest = new GraphQLRequest
{
Query = @"
query GetCommitsFirstPage($cursor: String, $commitsSince: GitTimestamp!, $repositoryName: String!, $repositoryOwner: String!) {
repository(name: $repositoryName, owner: $repositoryOwner) {
defaultBranchRef{
target {
... on Commit {
history(after: $cursor, since: $commitsSince) {
totalCount
pageInfo {
endCursor
hasNextPage
}
}
}
}
}
}
}",
OperationName = "GetCommitsFirstPage",
Variables = new
{
commitsSince = since.ToString("o"),
cursor = cursor,
repositoryOwner = repositoryOwner,
repositoryName = repositoryName
}
};
// Code omitted for brevity
}
public async Task<GetCommitsPageResponse> GetDefaultBranchCommitsPageByPeriodAsync(DateTime since, string cursor, string repositoryOwner, string repositoryName)
{
// Code omitted for brevity
var commitsRequest = new GraphQLRequest
{
Query = @"
query GetCommitsSinceTimestamp($cursor: String, $commitsSince: GitTimestamp!, $repositoryName: String!, $repositoryOwner: String!) {
repository(name: $repositoryName, owner: $repositoryOwner) {
defaultBranchRef{
target {
... on Commit {
history(after: $cursor, since: $commitsSince) {
pageInfo {
endCursor
hasNextPage
}
edges {
node {
oid
additions
deletions
commitUrl
url
committedDate
associatedPullRequests (first: 10) {
nodes {
id
mergedAt
}
}
repository {
databaseId
nameWithOwner
}
author {
name
email
user {
login
}
}
message
}
}
}
}
}
}
}
}",
OperationName = "GetCommitsSinceTimestamp",
Variables = new
{
commitsSince = since.ToString("o"),
cursor = cursor,
repositoryOwner = repositoryOwner,
repositoryName = repositoryName
}
};
// Code omitted for brevity
}https://stackoverflow.com/questions/63181325
复制相似问题