简体   繁体   中英

How can I get branch count on a repository via GitHub API?

I'm working on a UI which lists all repositories of a given user or organization. This is using a tree format, where the first level is the repositories, and the second level of hierarchy (child nodes) are to be each branch, if expanded.

I'm using a mechanism that deliberately doesn't require me to pull a list of all branches of a given repo, because the API has rate limits on API calls. Instead, all I have to do is instruct it how many child nodes it contains, without actually assigning values to them (until the moment the user expands it). I was almost sure that fetching a list of repos includes branch count in the result, but to my disappointment, I don't see it. I can only see count of forks, stargazers, watchers, issues, etc. Everything except branch count.

The intention of the UI is that it will know in advance the number of branches to populate the child nodes, but not actually fetch them until after user has expanded the parent node - thus immediately showing empty placeholders for each branch, followed by asynchronous loading of the actual branches to populate. Again, since I need to avoid too many API calls. As user scrolls, it will use pagination to fetch only the page(s) it needs to show to the user, and keep it cached for later display.

Specifically, I'm using the Virtual TreeView for Delphi:

procedure TfrmMain.LstInitChildren(Sender: TBaseVirtualTree; Node: PVirtualNode;
  var ChildCount: Cardinal);
var
  L: Integer;
  R: TGitHubRepo;
begin
  L:= Lst.GetNodeLevel(Node);
  case L of
    0: begin
      //TODO: Return number of branches...
      R:= TGitHubRepo(Lst.GetNodeData(Node));
      ChildCount:= R.I['branch_count']; //TODO: There is no such thing!!!
    end;
    1: ChildCount:= 0; //Branches have no further child nodes
  end;
end;

Is there something I'm missing that allows me to get repo branch count without having to fetch a complete list of all of them up-front?

You can use the new GraphQL API instead. This allows you to tailor your queries and results to just what you need. Rather than grabbing the count and then later filling in the branches, you can do both in one query.

Try out the Query Explorer .

query {
  repository(owner: "octocat", name: "Hello-World") {
    refs(first: 100, refPrefix:"refs/heads/") {
      totalCount
      nodes {
        name
      }
    },
    pullRequests(states:[OPEN]) {
        totalCount
    }
  }
}
{
  "data": {
    "repository": {
      "refs": {
        "totalCount": 3,
        "nodes": [
          {
            "name": "master"
          },
          {
            "name": "octocat-patch-1"
          },
          {
            "name": "test"
          }
        ]
      },
      "pullRequests": {
        "totalCount": 192
      }
    }
  }
}

Pagination is done with cursors. First you get the first page, up to 100 at a time, but we're using just 2 here for brevity. The response will contain a unique cursor.

{
  repository(owner: "octocat", name: "Hello-World") {
    pullRequests(first:2, states: [OPEN]) {
      edges {
        node {
          title
        }
        cursor
      }
    }
  }
}
{
  "data": {
    "repository": {
      "pullRequests": {
        "edges": [
          {
            "node": {
              "title": "Update README"
            },
            "cursor": "Y3Vyc29yOnYyOpHOABRYHg=="
          },
          {
            "node": {
              "title": "Just a pull request test"
            },
            "cursor": "Y3Vyc29yOnYyOpHOABR2bQ=="
          }
        ]
      }
    }
  }
}

You can then ask for more elements after the cursor. This will get the next 2 elements.

{
  repository(owner: "octocat", name: "Hello-World") {
    pullRequests(first:2, after: "Y3Vyc29yOnYyOpHOABR2bQ==", states: [OPEN]) {
      edges {
        node {
          title
        }
        cursor
      }
    }
  }
}

Queries can be written like functions and passed arguments . The arguments are sent in a separate bit of JSON. This allows the query to be a simple unchanging string.

This query does the same thing as before.

query NextPullRequestPage($pullRequestCursor:String) {
  repository(owner: "octocat", name: "Hello-World") {
    pullRequests(first:2, after: $pullRequestCursor, states: [OPEN]) {
      edges {
        node {
          title
        }
        cursor
      }
    }
  }
}

{
  "pullRequestCursor": "Y3Vyc29yOnYyOpHOABR2bQ=="
}

{ "pullRequestCursor": null } will fetch the first page.


Its rate limit calculations are more complex than the REST API. Instead of calls per hour, you get 5000 points per hour. Each query costs a certain number of points which roughly correspond to how much it costs Github to compute the results. You can find out how much a query costs by asking for its rateLimit information. If you pass it dryRun: true it will just tell you the cost without running the query.

{
  rateLimit(dryRun:true) {
    limit
    cost
    remaining
    resetAt
  }
  repository(owner: "octocat", name: "Hello-World") {
    refs(first: 100, refPrefix: "refs/heads/") {
      totalCount
      nodes {
        name
      }
    }
    pullRequests(states: [OPEN]) {
      totalCount
    }
  }
}
{
  "data": {
    "rateLimit": {
      "limit": 5000,
      "cost": 1,
      "remaining": 4979,
      "resetAt": "2019-08-21T05:13:56Z"
    }
  }
}

This query costs just one point. I have 4979 points remaining and I'll get my rate limit reset at 05:13 UTC.

The GraphQL API is extremely flexible. You should be able to do more with it using less Github resources and less programming to work around rate limits.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM