sebastian/git-state
sebastian/git-state is a small PHP library that inspects a Git checkout and reports its state: origin URL, current branch, commit hash, and whether the working directory is clean (or the current git status). Useful for build/test tooling.
Installation:
Add to composer.json (or run):
composer require sebastian/git-state
For dev-only (e.g., testing):
composer require --dev sebastian/git-state
First Usage:
Inject the Builder into a Service Provider, Artisan Command, or Controller:
use SebastianBergmann\GitState\Builder;
public function checkGitState()
{
$builder = new Builder();
$state = $builder->build();
if (!$state) {
return response()->json(['error' => 'Not a Git repo or missing origin'], 400);
}
return [
'branch' => $state->branch(),
'commit' => $state->commit(),
'is_clean' => $state->isClean(),
];
}
Quick Win: Use in a CI/CD pipeline to block dirty deploys:
// In a GitHub Actions step or Laravel command
$state = (new Builder())->build();
if (!$state || !$state->isClean()) {
throw new \RuntimeException('Deployment aborted: Uncommitted changes detected.');
}
Where to Look First:
Builder class: Core entry point for creating state snapshots.State object methods:
originUrl(): Remote repository URL.branch(): Current branch name.commit(): Latest commit hash.isClean(): Boolean for working directory status.status(): Raw git status output (if dirty).build() returns false for non-Git repos or missing origins.Enforce clean working directories for API requests:
namespace App\Http\Middleware;
use Closure;
use SebastianBergmann\GitState\Builder;
class GitStateMiddleware
{
public function handle($request, Closure $next)
{
$state = (new Builder())->build();
if (!$state || !$state->isClean()) {
return response()->json(['error' => 'Git validation failed'], 403);
}
return $next($request);
}
}
Register in app/Http/Kernel.php:
protected $middleware = [
\App\Http\Middleware\GitStateMiddleware::class,
];
Create a command to inspect Git state:
php artisan make:command GitStatus
// app/Console/Commands/GitStatus.php
protected function handle()
{
$state = (new Builder())->build();
if (!$state) {
$this->error('Not a Git repository or missing origin.');
return;
}
$this->info("Branch: {$state->branch()}");
$this->info("Commit: {$state->commit()}");
$this->info("Origin: {$state->originUrl()}");
if (!$state->isClean()) {
$this->error("Dirty working directory:\n" . $state->status());
}
}
Encapsulate logic in a service for dependency injection:
namespace App\Services;
use SebastianBergmann\GitState\Builder;
class GitService
{
public function getState()
{
$builder = new Builder();
$state = $builder->build();
return $state ? [
'branch' => $state->branch(),
'commit' => $state->commit(),
'is_clean' => $state->isClean(),
] : null;
}
}
Use in controllers:
public function showGitInfo(GitService $gitService)
{
return response()->json($gitService->getState());
}
Add a Git state check to Laravel’s app/Console/Kernel.php for pre-deployment:
protected function schedule(Schedule $schedule)
{
$schedule->command('git:validate')->before('deploy');
}
Create the command:
// app/Console/Commands/GitValidate.php
protected function handle()
{
$state = (new Builder())->build();
if (!$state || !$state->isClean()) {
$this->error('Git validation failed. Aborting deployment.');
exit(1);
}
$this->info('Git state validated successfully.');
}
Use commit hashes or branches to toggle features:
public function featureEnabled(Request $request)
{
$state = (new Builder())->build();
$commit = $state?->commit();
return $commit === 'abc123' ? true : false;
}
Laravel Facades: Create a facade for cleaner syntax:
// app/Providers/AppServiceProvider.php
public function boot()
{
$this->app->bind('git', function () {
return new Builder();
});
}
Usage:
$state = app('git')->build();
Caching Git State: Cache results in Redis for performance (e.g., in microservices):
use Illuminate\Support\Facades\Cache;
public function getCachedState()
{
return Cache::remember('git.state', now()->addMinutes(5), function () {
return (new Builder())->build();
});
}
Error Handling:
Wrap build() in a try-catch for robustness:
try {
$state = (new Builder())->build();
} catch (\Exception $e) {
Log::error("Git state check failed: " . $e->getMessage());
return false;
}
Testing:
Mock the Builder in PHPUnit:
$mockBuilder = Mockery::mock(Builder::class);
$mockBuilder->shouldReceive('build')->andReturn($mockState);
$this->app->instance(Builder::class, $mockBuilder);
Non-Git Directories:
build() returns false if not in a Git repo. Always check:
$state = (new Builder())->build();
if (!$state) { /* Handle error */ }
Missing Origin Remote:
originUrl() may return null if no remote is configured. Validate:
if (!$state->originUrl()) {
throw new \RuntimeException('No Git origin configured.');
}
Git Command Failures:
git commands may fail silently. Use Laravel’s Process facade for better error handling:
use Illuminate\Support\Facades\Process;
$output = Process::run('git status');
if ($output->failed()) {
Log::error("Git command failed: " . $output->errorOutput());
}
Permissions Issues:
.git directory is inaccessible (e.g., Docker permissions), build() will fail. Ensure:
chmod -R 755 .git).PATH.Dirty Working Directory:
isClean() returns false for staged/unstaged changes. Use status() to debug:
if (!$state->isClean()) {
Log::warning("Dirty status:\n" . $state->status());
}
Git Version Compatibility:
git --version
Network Dependencies:
originUrl() requires network access. In air-gapped environments, it may fail. Cache or mock:
$origin = Cache::remember('git.origin', now()->addHours(1), function () {
return (new Builder())->build()?->originUrl();
});
Log Raw Output:
Log the raw git status for debugging:
Log::debug("Git status: " . $state->status());
Check Git Environment: Verify Git is available in your environment:
which git
git --version
Mock for Testing: Use Mockery to simulate Git states in tests:
$mockState = Mockery::mock(\SebastianBergmann\GitState\State::class);
$mockState->shouldReceive('branch')->andReturn('main');
$mockState
How can I help you explore Laravel packages today?