directorytree/git
Lightweight PHP wrapper for running Git commands on a server. Supports pull, fetch, reset (hard/soft), and remote management (get/get all/add/set URL). Requires PHP 7.3+ and a working directory set to your repo via chdir().
composer require directorytree/git
chdir('/path/to/your/repo');
use DirectoryTree\Git\Git;
$git = new Git('origin'); // Specify remote (optional)
$success = $git->pull('main'); // Pulls 'main' branch
if ($success) {
// Handle success (e.g., update UI, log event)
}
Fetch latest tags/commits before operations:
$git->fetch();
$tags = $git->getTags(); // Get all tags
$latestTag = $git->getLatestTag(); // Get newest tag
Reset to a specific commit/tag (e.g., for deployments):
$git->reset('v1.2.0', 'hard'); // Force reset to tag
$git->setRemoteUrl('origin', 'https://new-repo-url.com');
$remotes = $git->getRemotes(); // Verify changes
$commits = $git->getCommitsBetween('a1b2c3', 'd4e5f6');
foreach ($commits as $commit) {
// Log commit messages or metadata
}
Laravel Artisan Commands:
// app/Console/Commands/DeployCommand.php
public function handle() {
$git = new Git();
$git->pull('production');
$this->info('Repository updated!');
}
Service Providers: Bind the Git client for dependency injection:
// app/Providers/AppServiceProvider.php
public function register() {
$this->app->singleton(Git::class, function () {
chdir(base_path());
return new Git('origin');
});
}
Event Listeners: Trigger actions on Git events (e.g., post-pull):
// app/Listeners/PostPullListener.php
public function handle() {
$git = resolve(Git::class);
if ($git->pull('main')) {
Cache::forget('repo_data');
}
}
Working Directory:
chdir() to the repo root before instantiating Git. Forgetting this causes commands to fail silently.getcwd() to verify the directory.Git Path Availability:
$PATH. Test locally with:
which git
Permission Issues:
reset or pull may fail due to file permissions. Use:
$git->reset('HEAD~1', 'soft'); // Test with soft reset first
Branch/Tag Sensitivity:
getCurrentTag() returns false if no tag exists. Validate with:
$tag = $git->getCurrentTag();
if ($tag === false) {
$this->warn('No tags found in repository.');
}
Command Output:
Enable verbose logging by extending the Git class:
class DebugGit extends Git {
protected function executeCommand(string $command): string {
$output = parent::executeCommand($command);
$this->log($command . "\n" . $output);
return $output;
}
}
Testing:
Use the built-in Terminal faker for unit tests:
Terminal::fake([
'git pull origin main' => Terminal::response()->successful(),
]);
$this->assertTrue($git->pull('main'));
Custom Commands:
Extend the class to add missing Git operations (e.g., checkout):
class ExtendedGit extends Git {
public function checkout(string $branch): bool {
return $this->execute('git checkout ' . $branch) === 0;
}
}
Configuration: Override default remotes or paths via a config file:
// config/git.php
return [
'default_remote' => env('GIT_REMOTE', 'origin'),
'repo_path' => env('GIT_REPO_PATH', base_path()),
];
Error Handling: Wrap commands in try-catch blocks for graceful failures:
try {
if (!$git->pull('main')) {
throw new RuntimeException('Git pull failed.');
}
} catch (Exception $e) {
Log::error($e->getMessage());
}
Cache Results: Store fetched tags/commits in Laravel’s cache:
$tags = Cache::remember('git.tags', now()->addHours(1), function () use ($git) {
return $git->getTags();
});
Batch Operations: For large repos, limit commit ranges:
$commits = $git->getCommits(['limit' => 100]);
How can I help you explore Laravel packages today?