adachsoft/gitlib

Lightweight, framework-agnostic Git library with flat facade and action dispatcher for PHP.

Maintainers

👁 Arkadiusz Adach

Package info

gitlab.com/a.adach/GitLib

Issues

pkg:composer/adachsoft/gitlib

Statistics

Installs: 28

Dependents: 2

Suggesters: 0

Stars: 0

v3.0.1 2026-05-26 21:17 UTC

Requires

Suggests

None

Provides

None

Conflicts

None

Replaces

None

MIT cf3c11126bf7cfb1112c87b2d5f11cf45843f158

  • Arkadiusz Adach

phpclilibrarygitvcs

This package is auto-updated.

Last update: 2026-06-27 03:38:09 UTC


README

Lightweight, framework-agnostic Git library with a flat facade and a name-based action dispatcher. Built for PHP 8.3+, PSR-compliant, easily extensible with plugin-like operation handlers.

Requirements

  • PHP 8.3+
  • Composer

Installation

composer require adachsoft/gitlib

Quick start

<?php

use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
use Adachsoft\GitLab\Internal\Adapter\CommandExecutorProcessRunner;
use Adachsoft\GitLab\Internal\Operation\HandlerLocator;
use Adachsoft\GitLab\Internal\Operation\OperationRegistry;
use Adachsoft\GitLab\Internal\GitRepository;
use Adachsoft\GitLab\DTO\Options\PullOptions;
use Adachsoft\GitLab\DTO\Options\PushOptions;
use Adachsoft\GitLab\Exception\ProcessFailedException;
use Adachsoft\GitLab\Exception\ValidationException;

$executor = new SimpleCommandExecutor();
$runner = new CommandExecutorProcessRunner($executor);

$locator = new HandlerLocator();
$registry = (new OperationRegistry())
 ->discoverAndRegisterHandlers($locator, $runner, __DIR__);

$repo = new GitRepository($runner, $registry, __DIR__); // working directory

try {
 // 1) Using the facade methods
 $status = $repo->status(); // Status DTO with file lists and repository state

 if ($status->state !== null && $status->state->isOperationInProgress()) {
 // handle ongoing merge/rebase/cherry-pick/revert
 }

 $repo->add(['file1.php', 'file2.php']);
 $repo->commit('Initial commit');

 $repo->pull(new PullOptions(remote: 'origin', branch: 'main', rebase: true));
 $repo->push(new PushOptions(remote: 'origin', branch: 'main', forceWithLease: true));

 // 2) Using the action dispatcher
 $repo->execute('getRemotes', ['verbose' => true]);
 $repo->execute('rebase', ['branch' => 'feature/xyz']);
 $repo->execute('push', [
 'remote' => 'origin',
 'branch' => 'main',
 'setUpstream' => true,
 ]);
} catch (ValidationException|ProcessFailedException $e) {
 // Handle invalid arguments or failed git processes
 echo $e->getMessage();
}

Client-level operations

In addition to working inside an existing repository, you can use GitClientInterface to create/clone repositories and check if a path is a Git repository.

<?php

use AdachSoft\CommandExecutorLib\SimpleCommandExecutor;
use Adachsoft\GitLab\Internal\Adapter\CommandExecutorProcessRunner;
use Adachsoft\GitLab\Internal\GitClient;
use Adachsoft\GitLab\DTO\Options\InitOptions;

$executor = new SimpleCommandExecutor();
$runner = new CommandExecutorProcessRunner($executor);
$client = new GitClient($runner);

// git clone git@gitlab.com:a.adach/embedding-contracts.git
$client->clone('git@gitlab.com:a.adach/embedding-contracts.git');

// git init --initial-branch=main --object-format=sha1
$client->init(new InitOptions(initialBranch: 'main', objectFormat: 'sha1'));

// check if directory is a Git repository
if ($client->isRepository('/path/to/repo')) {
 // ...
}

Mapping common Git commands

The facade API is designed to closely match common Git CLI commands. Examples:

<?php

use Adachsoft\GitLab\DTO\Options\InitOptions;
use Adachsoft\GitLab\DTO\Options\PushOptions;

// git config --local user.name "Arek"
$repo->setConfig('user.name', 'Arek', 'local');

// git config --local user.email "adachsoft@gmail.com"
$repo->setConfig('user.email', 'adachsoft@gmail.com', 'local');

// git switch --create main (equivalent)
$repo->createBranch('main');

// git push --set-upstream origin main
$repo->push(new PushOptions(remote: 'origin', branch: 'main', setUpstream: true));

// git init --initial-branch=main --object-format=sha1
$repo->init(new InitOptions(initialBranch: 'main', objectFormat: 'sha1'));

// git remote add origin git@gitlab.com:a.adach/embedding-contracts.git
$repo->addRemote('origin', 'git@gitlab.com:a.adach/embedding-contracts.git');

Repository status

GitRepository::status() returns a Status DTO that describes the current branch, file changes and high-level repository state.

The DTO contains:

  • branch (string): current branch name.
  • staged (string[]): files staged for commit.
  • modified (string[]): modified but unstaged files.
  • untracked (string[]): untracked files.
  • deleted (string[]): deleted files.
  • conflicted (string[]): paths that are part of merge conflicts.
  • rawOutput (string): raw git status --porcelain output.
  • state (RepositoryStateVo|null): flags describing in-progress operations.

RepositoryStateVo exposes four boolean flags and a convenience method:

  • mergeInProgress
  • rebaseInProgress
  • cherryPickInProgress
  • revertInProgress
  • isOperationInProgress()true if any of the above is true.

Example usage:

$status = $repo->status();

if ($status->conflicted !== []) {
 // handle conflicted files
}

if ($status->state !== null && $status->state->isOperationInProgress()) {
 // react to an ongoing merge/rebase/cherry-pick/revert
}

Concepts

  • Facade: GitRepositoryInterface exposes a full set of explicit Git operations (no magic execute() here).
  • Dispatcher: GitActionExecutorInterface::execute(string $action, array $args) allows dynamic invocation by action name.
  • Handlers: Each action is handled by a class implementing OperationHandlerInterface and marked with #[GitOperation('actionName')].
  • Process execution: Abstracted via ProcessRunnerInterface with the default adapter CommandExecutorProcessRunner (wraps adachsoft/command-executor-lib).
  • DTOs: Simple final readonly objects (no getters) for structured results.

Auto-discovery of handlers

HandlerLocator discovers and instantiates handlers marked with #[GitOperation] in the Adachsoft\GitLab\Extensions namespace (filesystem defaults to src/Extensions). You can customize the directory and namespace if needed.

$registry->discoverAndRegisterHandlers($locator, $runner, $workingDirectory);

Adding a custom action

1) Create a handler in src/Extensions:

<?php

namespace Adachsoft\GitLab\Extensions;

use Adachsoft\GitLab\Attributes\GitOperation;
use Adachsoft\GitLab\Contracts\ProcessRunnerInterface;
use Adachsoft\GitLab\Contracts\OperationHandlerInterface;

#[GitOperation('hello')]
final class HelloHandler extends AbstractOperationHandler implements OperationHandlerInterface
{
 public function name(): string { return 'hello'; }
 public function handle(array $arguments)
 {
 $this->processRunner->run('git status', $this->workingDirectory);
 return 'world';
 }
}

2) Let the locator discover it (nothing else to change).

Exceptions

  • ValidationException – invalid arguments/options passed to operations/handlers.
  • ProcessFailedException – underlying git command failed (non-zero exit, etc.).

Notes

  • Framework-agnostic: no container required; wire objects manually.
  • PSR style; PHPDoc/comments in English.
  • See docs/tasks/task001-plan-v1.md for the original design plan and acceptance criteria.