klaussilveira/gitter
Gitter is a lightweight PHP client for the GitHub API, offering a fluent, object-oriented interface to repositories, users, issues, and more. It handles authentication, pagination, and HTTP concerns so you can focus on building GitHub-powered features quickly.
klaussilveira/gitter is a small, read-oriented object wrapper around the git CLI. You give it a path to a repository on disk, and it returns commit, branch, tag, tree, blob, diff, and statistics objects. There is no framework integration, no service provider, and no database — just Composer autoloading and a git binary.
Prerequisites to verify before writing any code:
composer.json; the codebase is pre-PHP-8 style and may not be fully typed)git installed and on PATH for the web process user (often www-data, not your shell user).git folder for that same userInstall:
composer require klaussilveira/gitter
Smallest useful example — list recent commits:
use Gitter\Client;
$client = new Client();
$repository = $client->getRepository(base_path('repos/my-app'));
foreach ($repository->getCommits() as $commit) {
printf("%s %s %s\n",
substr($commit->getHash(), 0, 8),
$commit->getAuthor(),
$commit->getMessage()
);
}
Where to look first:
src/Gitter/Client.php — how git commands are executed and how the binary path is configured.src/Gitter/Repository.php — the primary API surface: getCommits(), getCommit(), getBranches(), getTags(), getTree(), getBlob(), getDiff(), getStatistics(), getCurrentBranch().src/Gitter/Model/ — the plain value objects (Commit, Branch, Tag, Tree, Blob, Diff, Statistics). Skim their getters so you know what you actually get back.First use case to validate the package against: "show the last N commits on branch X with insertions/deletions per commit." That single flow exercises listing, pagination, ref handling, and diff/stat parsing, and will quickly expose whether the shell-out model and parsing quality are acceptable for your app.
Repository as a scoped service. In Laravel, bind a single Client as a singleton, and resolve a Repository per request from a configured base path. Keep the raw gitter objects out of controllers; wrap them in your own RepoReader/ChangelogService so tests can swap in fakes and so you control caching and error handling in one place.
$this->app->singleton(Client::class, fn () => new Client());
// In a service:
public function commits(string $branch, int $page = 1, int $perPage = 20): array
{
return $this->client
->getRepository($this->repoPath)
->getCommits($branch, ($page - 1) * $perPage, $perPage);
}
Commit browsing / changelog pages. The dominant pattern is paginate → iterate → render. Always pass explicit offset and limit; the default limit is small and will silently truncate lists. Resolve the branch or ref yourself (getCurrentBranch() or a route parameter) and pass it consistently.
Tree and blob traversal. getTree() returns entries that are either blobs or subtrees. Check each entry's type/mode before recursing, and pass hash values — not paths — to getBlob() and getTree(). Recursive file browsers should be built with a depth cap and a cancellation/size guard.
Diff and stat views. getDiff($commit) and getStatistics($commit) are the two calls behind a "files changed" tab. Render diff lines by their type (added / removed / unchanged) rather than diffing strings yourself, and use statistics for summary badges. Resolve the parent commit first if you want first-parent diffs on merges.
Caching aggressively. Every call shells out to git. Cache commit lists per (repo, ref, offset, limit), commit objects per hash, and tree listings per tree hash with a short TTL. Invalidate by comparing getCurrentBranch()/HEAD or simply use a 1–5 minute TTL for dashboards.
Testing pattern. Don't point unit tests at your real repository. Create a temp directory per test, run git init and a few commits through Symfony\Component\Process\Process, then exercise gitter against it. This also documents exactly which git version behavior you depend on.
Read-only by design. This library is for inspection. Deploy tooling, commit creation, branch checkout, merges, and pushes are not part of the API — use symfony/process or git directly for those and keep gitter for the read side.
setGitBinary('/usr/bin/git')) instead of debugging exec.git init-ed repo with no commits will fail on getCommits(). Guard for this before rendering a UI.getDate() / getAuthorDate() return a DateTime or a formatted string before calling ->format(). Normalize to DateTimeImmutable at your boundary either way.getBlob()->getContent() will happily return binary bytes. Size-check and MIME-sniff before sending content to a browser.getRepository(). Resolve with realpath() and assert it starts with a known repository root; otherwise you have an arbitrary-read primitive over the filesystem.git log/git show/git diff manually in the same directory as the same OS user. That distinguishes a gitter parsing bug from a permissions or git-version issue.Repository and the model objects over subclassing; Client and Repository have few seams, and the model classes are simple value objects.How can I help you explore Laravel packages today?