Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Commit History Laravel Package

spiriitlabs/commit-history

Laravel package that brings Git commit history into your application, with a simple built-in interface to browse recent commits, inspect changes, and keep project history accessible to your team without leaving the admin area.

View on GitHub
Deep Wiki
Context7

Getting Started

composer require spiriitlabs/commit-history

The package is auto-discovered by Laravel. On a fresh install, publish the config so you can see the provider/credential keys the library expects:

php artisan vendor:publish --tag=commit-history-config

Add credentials to .env (the library reads GitHub/GitLab tokens from config):

COMMIT_HISTORY_GITHUB_TOKEN=
COMMIT_HISTORY_GITLAB_TOKEN=

Where to look first: open the published config/commit-history.php and the package README's "Usage" section side by side. The README is the authoritative source for the exact class/method names (e.g. a CommitHistory entry point or a provider factory) — confirm the factory names before wiring anything to your app, because that surface is small and easy to adapt to.

First use case: render recent activity on an internal dashboard. A minimal controller:

use SpiriitLabs\CommitHistory\CommitHistory;

Route::get('/repo/{repo}/commits', function (CommitHistory $history, string $repo) {
    return $history
        ->for($repo)          // "owner/repo" or "group/project"
        ->from('github')      // provider
        ->get();              // collection of commits
});

If your README's method names differ, the concept is identical: build a history from a repo identifier + provider, then iterate commits (SHA, message, author, date). Treat this first call as a smoke test that your token and repo identifier resolve correctly before you build anything on top of it.

Implementation Patterns

Wrap it in a dedicated service. Don't call the library from controllers or Blade. A thin CommitRepository service gives you a single seam to mock in tests and to swap providers:

final class CommitRepository
{
    public function __construct(private CommitHistory $history) {}

    public function recent(string $repo, string $provider = 'github'): array
    {
        return $this->history->for($repo)->from($provider)->get()
            ->map(fn ($c) => [
                'sha'     => $c->sha ?? $c->hash,
                'message' => $c->message,
                'author'  => $c->author,
                'date'    => $c->date,
            ])->all();
    }
}

Persist to a table for anything user-facing. Hitting the GitHub/GitLab API per page view will burn rate limits fast. Sync commits into a commits table keyed by repo + sha and render from the database:

foreach ($commits as $commit) {
    Commit::updateOrCreate(
        ['repository' => $repo, 'sha' => $commit->sha],
        ['message' => $commit->message, 'author' => $commit->author, 'committed_at' => $commit->date]
    );
}

updateOrCreate on sha makes re-runs idempotent — important because you'll re-run the sync on a schedule.

Drive it from a scheduled command, not a request.

// app/Console/Commands/SyncCommits.php
foreach (config('repositories.tracked') as $repo) {
    SyncRepositoryCommits::dispatch($repo);
}
// routes/console.php
Schedule::command('commits:sync')->hourly();

Dispatch a queued job per repo so one slow/failing repo doesn't block the others, and give the job a uniqueFor / WithoutOverlapping guard to prevent pile-ups if a previous run is still going.

Abstract the provider. Config-drive which provider a repo uses so the same service handles GitHub and GitLab:

// config/repositories.php
return [
    'tracked' => [
        'acme/api'   => ['provider' => 'github'],
        'acme/infra' => ['provider' => 'gitlab'],
    ],
];

Use it for release notes / changelog generation. Filter commits since a tag or date and pass them to a formatter or AI summarizer — this is where the package shines compared to hand-rolling HTTP calls.

Cache read paths. Wrap dashboard reads in Cache::remember("commits.{$repo}", now()->addMinutes(15), ...) rather than caching the API call itself; the DB is your cache of record.

Gotchas and Tips

  • Rate limits are the whole game. Unauthenticated GitHub is ~60 req/hr; authenticated is 5,000/hr. Always send a token in CI/production. GitLab has similar per-user limits. Log the remaining-quota headers (GitHub exposes X-RateLimit-Remaining) so you can debug 403s quickly.
  • Token scopes matter. A GitHub token without repo scope silently returns nothing for private repos (or 404s). Same for GitLab read_repository vs api scopes. If you get an empty collection with no exception, suspect scope first.
  • Repo identifier format differs per provider. GitHub wants owner/repo; GitLab is often group/subgroup/project and self-hosted GitLab needs a custom base URL in config. Don't assume one string works everywhere — normalize in your own service.
  • Pagination is not free. Most of these libraries return one page by default. If you need a full history, pass an explicit per-page/max and loop, or you'll quietly miss commits on active repos. Check the README for the pagination parameter — it's usually the thing people overlook.
  • Deduplicate by SHA, not by message. Squash-merges, rebases and force-pushes produce duplicate-looking messages; SHA (or GitLab's commit id) is your only stable key. Store it as an indexed column.
  • Timezone drift. Commit dates come back as ISO-8601 with offsets. Cast consistently (e.g. store UTC, display in the user's tz) or your "recent activity" list will look out of order.
  • Test with Http::fake(), not the real API. The library ultimately makes HTTP calls, so Http::fake(['api.github.com/*' => Http::response($fixture)]) gives you deterministic tests without tokens. Build fixtures from one real response and reuse them.
  • Handle provider outages gracefully. Wrap syncs in try/catch, log the failure, and let the stale DB rows serve the UI. Never let a scheduled sync throw uncaught — Laravel will just keep retrying into the rate-limit wall.
  • Watch for silent 404s. A renamed repo returns 404, not an error you'll notice without logging. Assert that the returned collection is non-empty during sync and alert when a previously-populated repo suddenly returns zero commits.
  • Extension point: if per-provider behavior diverges (e.g. GitLab pipelines vs GitHub checks), don't fork the library — keep the provider-specific bits in your own adapter classes behind a shared interface, and treat the package purely as the transport layer.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib