NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Git for Laravel

Branches & commits

Branches come back as a Page of branch names:

$branches = Git::github()->repo('acme/api')->branches()->items; // ['main', 'dev']

A branch head by exact name

branch() returns one branch head — a Branch with its name and sha — on GitHub, GitLab and Bitbucket. Unlike commit($ref), which resolves any commit-ish, it answers only to that exact branch name: a tag, a sha or a mere prefix of a branch name is the usual 404 RequestException, never another commit — so a deploy that goes by branch name can’t be handed a tag’s commit:

$branch = Git::github()->repo('acme/infra')->branch('main'); // Branch — by its exact name

$branch->name;   // "main"
$branch->sha;    // the commit the branch points at
  • GitHub is read through /git/ref/heads/{branch}, not /branches/{branch}, which answers a renamed branch with a 301 that would follow it to another head.
  • An answer that isn’t exactly that branch’s head — on GitHub anything but refs/heads/<name> pointing at a commit with a 40-character sha — throws UnexpectedValueException.
  • The name goes through the ref checks before any request (no traversal, no whitespace). A GitHub App token needs contents: read.

The commit query

commits() returns a chainable CommitQuery. Every filter is optional and URL-encoded for you:

$repo = Git::github()->repo('acme/api');

$commits = $repo->commits()               // CommitQuery
    ->branch('feature/new ui')            // properly URL-encoded
    ->author('jane-doe')
    ->path('src/')
    ->since(now()->subWeek())             // Carbon or an ISO 8601 string
    ->until(now())
    ->perPage(50)
    ->get();                              // Page<Commit>

$repo->commits()->get(page: 2);             // a specific page
$repo->commits()->collect();                // Collection<Commit>
$repo->commits()->branch('main')->first();  // ?Commit

foreach ($repo->commits()->branch('main')->lazy() as $commit) {
    // auto-paginated stream
}
  • branch(), author(), path() — narrow by ref, author and file path.
  • since() / until() — a Carbon instance or an ISO 8601 string.
  • perPage() — page size; get($page) — one Page<Commit>; collect($page) — that page as a Collection.
  • first() — the first matching commit or null; lazy() — a LazyCollection across every page.

GitHub and GitLab apply every filter. Bitbucket’s commits endpoint filters by branch() and path() only, so on Bitbucket author(), since() and until() throw FeatureNotSupportedException when the query runs, rather than returning unfiltered commits.

A single commit

$commit = Git::github()->repo('acme/api')->commit('6dcb09b...');

$commit->sha;
$commit->message;
$commit->author->name;   // Author: name, email, avatar
$commit->author->email;
$commit->url;            // ?string
$commit->commitAt;       // Carbon

Repository activity

activity() reads GitHub’s repository activity feed, newest first — pushes, force pushes, branch creations and deletions, pull request and merge queue merges — for the whole repository or one ref (main or refs/heads/main):

$page = Git::github()->repo('acme/infra')->activity('refs/heads/main', perPage: 50); // Page<Activity>

foreach ($page as $activity) {
    $activity->type;          // ActivityType::Push, ForcePush, BranchCreation, BranchDeletion, …
    $activity->ref;           // e.g. "refs/heads/main"
    $activity->before;        // ?string — null for a branch creation
    $activity->after;         // ?string — null for a branch deletion
    $activity->actor?->name;  // who did it, or null
    $activity->occurredAt;    // Carbon
}

$next = $page->hasMore ? Git::github()->repo('acme/infra')->activity('main', 50, $page->nextCursor) : null;
  • It is cursor-paged: GitHub takes no page number here, so hand the page’s nextCursor back as the third argument while hasMore is true. perPage is capped at 100.
  • before is null for a branch creation and after for a deletion (GitHub’s all-zero sha); actor is null when GitHub names nobody; a type GitHub adds later reads as ActivityType::Unknown.
  • A 403 or 404 — an installation that may not read the feed — stays a RequestException, so you can fall back to commits(). A token that can read the repository’s contents can read it.
  • GitHub only (Feature::RepositoryActivity); GitLab and Bitbucket throw FeatureNotSupportedException.

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 crypto

By 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.