Pull requests, issues & files
Every other read follows the same pattern — lists return a Page, single resources return a typed object:
$repo = Git::github()->repo('acme/api');
$repo->pullRequests(); // Page<PullRequest>, open by default
$repo->pullRequests('closed', perPage: 50);
$repo->pullRequest(7)->get(); // GitLab MRs, Bitbucket PRs
$repo->issues(); // Page<Issue>
$repo->issue(12);
$repo->tags(); // Page<Tag>
$repo->releases(); // Page<Release>
$repo->release('v1.0'); // by tag or id
$repo->contents('README.md', ref: 'main'); // FileContent (decoded); no ref = the default branch
$repo->compare('main', 'feature'); // Comparison
$repo->contributors(); // Page<Contributor>
$repo->languages(); // ['PHP' => 12345, ...]pullRequests() and issues() take a state (open by default); the package translates it into each forge’s own spelling, so open means opened on GitLab and OPEN on Bitbucket, and closed asks Bitbucket for DECLINED and SUPERSEDED alike. GitLab merge requests and Bitbucket pull requests come back as the same PullRequest object. contents() without a ref reads the default branch on every forge.
- GitHub’s issues() lists issues only — its endpoint returns pull requests too, so they are dropped; hasMore still follows the Link header, so a page can hold fewer than perPage items. issue() with a pull request’s number throws the same 404 RequestException as a missing issue.
- GitHub’s release() tries the tag first, then — for an all-digit value no tag answers to — the numeric release id.
- GitHub’s contents() returns the whole file up to 100 MB, reading files over 1 MB through the blob endpoint. A directory path throws OutOfScopeException naming the path.
- compare() also returns the commits between the two refs, oldest first, with totalCommits and — on GitHub — status: ComparisonStatus Ahead, Behind, Identical or Diverged. GitHub sends the newest 250 commits but counts all of them, so totalCommits above count($diff->commits) means the list was cut; files stop at 300. GitLab fills commits and totalCommits but leaves status null, since it can’t tell diverged from ahead — as is a status GitHub hasn’t documented.
Files and diffs
$repo = Git::github()->repo('acme/api');
$file = $repo->contents('composer.json', ref: 'main');
$file->path; // "composer.json"
$file->content; // the decoded file body
$file->sha; // blob sha — what updateFile() needs
$file->size; // bytes
$diff = $repo->compare('main', 'feature/ci');
$diff->aheadBy;
$diff->behindBy;
$diff->status; // ?ComparisonStatus — Ahead, Behind, Identical or Diverged (GitHub)
$diff->totalCommits; // ?int — every commit between the two refs
$diff->commits; // list<Commit>, oldest first
$truncated = $diff->totalCommits > count($diff->commits); // GitHub sends the newest 250
foreach ($diff->files as $changed) { // list<ComparisonFile>
$changed->filename;
$changed->status; // e.g. "modified"
$changed->additions;
$changed->deletions;
}Result objects
| Object | Fields |
|---|---|
PullRequest | provider, id, number, title, body, state, sourceBranch, targetBranch, author, url, createdAt, draft, headSha, headRepository |
Issue | provider, id, number, title, body, state, author, url, createdAt |
Tag | provider, name, sha, url |
Release | provider, id, tagName, name, body, draft, prerelease, url, createdAt |
FileContent | path, content (decoded), sha, size, url |
Comparison | base, head, aheadBy, behindBy, files (list of ComparisonFile: filename, status, additions, deletions), status (ComparisonStatus), totalCommits, commits (list of Commit, oldest first) |
Branch | provider, name, sha |
Activity | provider, id, type (ActivityType), ref, before, after, actor, occurredAt |
Contributor | id, name, avatar, contributions |
Comment | id, body, author, url, createdAt |
Author / Owner | name, email, avatar / id, name, avatar |
Capabilities differ per provider — Bitbucket has no issues, tags or releases here, for instance. Calling one a provider doesn’t support throws FeatureNotSupportedException; see Feature detection to branch on support instead.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.