cypresslab/gitelephant
GitElephant is a PHP abstraction layer for managing Git repositories. It wraps the git binary in an object-oriented API, letting you inspect history, branches, tags, commits, and files without handling raw command output. Requires PHP 7.2+ and Git on a Unix-like system.
>=8.1 floor, Symfony ^6.4 || ^7.0 || ^8.0, PHPUnit ^10.5 || ^11.0, autoloading moved from psr-0 to psr-4, annotations converted to PHP 8 attributes, Rector ^2.0 / PHPStan ^2.0 / EasyCodingStandard ^12 || ^13.6.4 effectively drops Laravel 9-era dependency sets and anything pinned to Symfony <6.4. Laravel 10+ is the realistic target. The PSR-0 → PSR-4 switch is the one change with a real chance of breaking consumers that relied on legacy classmap/class-name assumptions.matteosister/gitelephant (maintained by the community, this release via @GenieTim), not cypresslab/gitelephant. Install command below is corrected. License should be re-verified against the repo before forking plans — historically MIT, not LGPL-3.0 as previously stated.exec() calls leaking into application code.exec('git …') (no safety net), league/git (less active), or the Git host's REST API (GitHub/GitLab) where the repo is remote. GitElephant remains the best fit only when the repo is local to the app filesystem.symfony/process (now ^6.4 || ^7.0 || ^8.0), which is already present in every Laravel app. The practical consequence is that GitElephant v5 and the host Laravel app must agree on a Symfony major, so Composer resolution is the main integration risk, not code.use statements. It only bites if we (a) reference internal classes by non-PSR-4 path, (b) maintain a classmap override, or (c) ship a custom autoloader for the package. Audit before upgrading.composer update pulls v5 and fails resolution against a Laravel app still on Symfony 6.0–6.3, or fails at runtime on PHP 8.0.matteosister/gitelephant:^4.6 until the app is on PHP 8.1+ and Symfony 6.4+; upgrade deliberately, not incidentally.composer check-platform-reqs step.composer why-not symfony/process 6.4 during planning; treat as a dependency-debt signal.git rev-parse --is-inside-work-tree guard) rather than trusting the constructor.diff()/log() are synchronous and can block a request. Offload to queues..git on disk. Path traversal and permission scope remain the primary exposure.>=8.1 and a Symfony >=6.4 resolution (i.e., Laravel 10+)? If not, v5 is blocked and v4.6 stays pinned.composer update? (Pin accordingly either way.)GitService rather than a global facade to keep testability. Artisan commands (git:status, git:pull) and events (GitCommitCreated) remain the natural integration points.>=8.1 — hard requirement.^6.4 || ^7.0 || ^8.0 — Laravel 10 (Symfony 6.4) through Laravel 12 (Symfony 7.x) are in range. Laravel 9 is out in practice and EOL anyway.^10.5 || ^11.0 — only relevant if we consume their test utilities.composer require matteosister/gitelephant:^5.0 (corrected from the prior cypresslab/...).composer why-not symfony/process "^6.4" — confirm nothing pins Symfony <6.4.^5.0, run the full suite plus a manual smoke test of every Git call site (status, log, diff, checkout, commit). Any failure here is almost certainly autoloading or Symfony resolution, not Git behavior.exec('git …') call behind a GitService wrapper:
// Before
exec('git checkout main', $output);
// After
$git = app(GitRepository::class); // bound in a service provider
$git->checkout('main');
config/git.php) sourced from .env, and add queue-backed execution for slow operations..git must be locally accessible — object-store disks (S3) are not viable for live repo operations.vendor/. Scope analysis to app/ to avoid noise.diff()/log() calls. Cache commit metadata; queue fetch/pull.Storage::lock(), which is not a real API):
Cache::lock("git-repo:{$repoPath}", 30)->block(10, function () use ($git, $message, $repoPath) {
$git->addAll($repoPath);
$git->commit($message, $repoPath);
});
A database semaphore works equally well if the cache store isn't shared.| Failure Scenario | Impact | Mitigation |
|---|---|---|
| Symfony constraint conflict on upgrade | Composer resolution fails; deploy blocked | Pre-flight why-not check; pin ^4.6 until resolved |
| PHP 8.0 host or CI image | Hard runtime/install failure | Platform audit; check-platform-reqs in CI |
| PSR-0 → PSR-4 autoload regression | Class-not-found on Git entry points | Smoke-test every call site post-upgrade; grep internals |
| Corrupt Git repo | Exceptions or silent failures | Health check on boot (rev-parse --is-inside-work-tree) |
Permission denied on .git |
All operations fail | Correct filesystem ownership/permissions in deploy |
| Disk full during clone | Partial repo, unhelpful error | Disk-space alerting; cleanup job |
| Remote timeout on pull/push | Hung worker | Process timeouts + exponential-backoff retries |
| Concurrent writes to one repo | Index lock errors, detached HEAD | Atomic cache lock / single-writer queue |
| Worker killed mid-operation | Partial commit, dirty worktree | Pre-flight status() check + recovery script |
exec() calls; (2) repo path safety — no ../, no user-supplied absolute paths; (3) detached HEAD and merge-conflict handling; (4) exception → log → user-message mapping..git permission model, disk-space and lock-contention alerts.storage/app/git/, one commit, one diff, one branch checkout, executed through the service layer under a held lock — proves the upgrade, the binding, and the concurrency guard in one pass.How can I help you explore Laravel packages today?