Pitmaster

PHP API

Pitmaster\Pitmaster

The Pitmaster\Pitmaster class is the static entry point. It provides factory methods for opening, creating, and cloning repositories, plus detection helpers.

use Pitmaster\Pitmaster;

open

public static function open(string $path): Repository

Open an existing repository at $path. The path should be the working tree root (containing .git/). Also supports linked worktrees (where .git is a file containing gitdir: <path>), and bare repositories (where $path is the .git directory itself).

Throws InvalidArgumentException if the path is not a git repository.

$repo = Pitmaster::open('/home/user/project');

init

public static function init(string $path): Repository

Initialize a new repository at $path. Creates the .git directory structure with objects/, refs/heads/, refs/tags/, HEAD (pointing to refs/heads/main), and a default config.

Throws RuntimeException if a repository already exists at the path.

$repo = Pitmaster::init('/home/user/new-project');

clone

public static function clone(string $url, string $path): Repository

Clone a remote repository via smart HTTP. Initializes a new repo, discovers remote refs, fetches the pack file, sets up remote tracking branches, and materializes the working tree.

$repo = Pitmaster::clone('https://github.com/user/repo.git', '/tmp/repo');

isRepository

public static function isRepository(string $path): bool

Check if a path is a git repository. Returns true for regular repos (.git/ directory), linked worktrees (.git file), and bare repos (HEAD file present).

if (Pitmaster::isRepository('/path/to/check')) {
    // It's a git repo
}

isWorktree

public static function isWorktree(string $path): bool

Check if a path is a linked worktree (not the main repo). Returns true only if .git is a file containing a gitdir: reference.

if (Pitmaster::isWorktree('/path/to/worktree')) {
    // It's a linked worktree
}

commonGitDir

public static function commonGitDir(string $path): ?string

Resolve the common git directory from any checkout path. For regular repos, this is the .git directory. For linked worktrees, this is the shared git directory that contains objects, config, and packed-refs. Returns null if the path is not a repository.

$common = Pitmaster::commonGitDir('/path/to/worktree');
// '/path/to/main-repo/.git'

Pitmaster\Repository

The Pitmaster\Repository class is the main handle for all git operations. Obtain one via Pitmaster::open(), Pitmaster::init(), or Pitmaster::clone().

use Pitmaster\Repository;

Path accessors

gitDir

public function gitDir(): string

Returns the per-worktree git directory. For regular repos this is .git/. For linked worktrees, this is .git/worktrees/<name>/.

commonGitDir

public function commonGitDir(): string

Returns the shared git directory. Same as gitDir() for regular repos. For linked worktrees, this is the main repo's .git/ directory (where objects, config, and packed-refs live).

workDir

public function workDir(): string

Returns the working tree root path.

isLinkedWorktree

public function isLinkedWorktree(): bool

Returns true if this repository handle is a linked worktree.


Objects

readObject

public function readObject(string $hash): GitObject

Read any object by its full 40-character hex hash. Returns a Blob, Tree, Commit, or Tag instance. Searches loose objects first, then pack files.

Throws ObjectNotFoundException if the hash is not found.

$object = $repo->readObject('a1b2c3d4e5f6...');

writeObject

public function writeObject(GitObject $object): ObjectId

Write an object to the loose object store. Returns the computed ObjectId.

use Pitmaster\Object\Blob;

$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);

catFile

public function catFile(string $hash): string

Return the raw content of an object, equivalent to git cat-file -p.

echo $repo->catFile('a1b2c3d4...');

objectExists

public function objectExists(string $hash): bool

Check if an object exists in the repository (loose or packed).

listObjects

public function listObjects(): array

List all object hashes in the repository. Returns an array of hex strings.


Refs

public function head(): Commit

Returns the current HEAD commit. Follows symbolic refs (HEAD -> refs/heads/main -> commit hash).

Throws RuntimeException if HEAD does not point to a valid commit (e.g., empty repository).

branch

public function branch(?string $name = null): ?string

Without arguments, returns the current branch name (e.g., 'main'), or null if HEAD is detached.

With a branch name, resolves it to its commit hash (hex string) or returns null if the branch does not exist.

$current = $repo->branch();        // 'main'
$hash = $repo->branch('feature');   // 'abc123...' or null

branches

public function branches(): array

Returns a sorted array of all branch names (without the refs/heads/ prefix).

$branches = $repo->branches();
// ['bugfix/typo', 'feature/login', 'main']

tags

public function tags(): array

Returns a sorted array of all tag names (without the refs/tags/ prefix).

resolve

public function resolve(string $revision): ObjectId

Resolve a revision expression to an ObjectId. Supports:

  • Full 40-character hex hashes
  • HEAD
  • Branch names (main, feature/login)
  • Tag names (v1.0.0)
  • Full ref paths (refs/heads/main)
  • Revision expressions (HEAD~3, main^2)

Throws RuntimeException if the revision cannot be resolved.

$id = $repo->resolve('HEAD~3');
$id = $repo->resolve('v1.0.0');
$id = $repo->resolve('feature/login');

allRefs

public function allRefs(): array

Returns all refs as an associative array of ref name to hex hash.

$refs = $repo->allRefs();
// ['refs/heads/main' => 'abc123...', 'refs/tags/v1.0.0' => 'def456...']

updateRef

public function updateRef(string $name, ObjectId $target): void

Update a ref to point to a new target.

$repo->updateRef('refs/heads/main', $commitId);

createBranch

public function createBranch(string $name, ?ObjectId $from = null): void

Create a new branch. If $from is null, branches from the current HEAD.

$repo->createBranch('feature/new-thing');
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));

deleteBranch

public function deleteBranch(string $name): void

Delete a branch by name.

$repo->deleteBranch('feature/old');

createTag

public function createTag(string $name, string $message, ?ObjectId $target = null, ?string $tagger = null): ObjectId

Create an annotated tag. If $target is null, tags the current HEAD. Returns the tag object's ObjectId.

$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');

defaultBranch

public function defaultBranch(): string

Resolve the repository's default branch. Checks the remote HEAD symref first, then the local HEAD, then falls back to main or master.

isBranchMerged

public function isBranchMerged(string $branch, ?string $target = null): bool

Check if a branch is fully merged into another branch (defaults to the default branch).

if ($repo->isBranchMerged('feature/done')) {
    $repo->deleteBranch('feature/done');
}

checkout

public function checkout(string $target): void

Switch branches or detach HEAD. Updates HEAD, the index, and the working tree.

$repo->checkout('feature/login');
$repo->checkout('abc123def456...');  // Detached HEAD

Index

index

public function index(): Index

Read and return the current index (staging area) from .git/index.

add

public function add(string ...$paths): void

Stage files. Reads each file from the working tree, creates a blob object, and updates the index.

$repo->add('src/Feature.php', 'tests/FeatureTest.php');

remove

public function remove(string ...$paths): void

Unstage files (equivalent to git rm --cached). Removes entries from the index without deleting the working tree files.

$repo->remove('old-file.txt');

mv

public function mv(string $source, string $destination): void

Move/rename a file in both the working tree and the index.

$repo->mv('old-name.php', 'new-name.php');

Commits

commit

public function commit(string $message, ?string $author = null): ObjectId

Create a commit from the current index. Builds a tree hierarchy from the index entries, creates the commit object with the HEAD as parent (if any), and updates HEAD. Returns the new commit's ObjectId.

The $author parameter is a full git author string (Name <email> timestamp timezone). If null, it is derived from .git/config or the PITMASTER_AUTHOR_NAME/PITMASTER_AUTHOR_EMAIL constants.

$id = $repo->commit('Fix null pointer bug');

show

public function show(string $revision): array

Show a commit's metadata and diff against its parent. Returns ['commit' => Commit, 'diff' => DiffResult[]].

$result = $repo->show('HEAD');
echo $result['commit']->message;

foreach ($result['diff'] as $diff) {
    echo $diff->format();
}

reset

public function reset(string $revision, string $mode = 'mixed'): void

Reset HEAD to a commit. Modes:

  • 'soft': move HEAD only
  • 'mixed': move HEAD + reset index (default)
  • 'hard': move HEAD + reset index + reset working tree
$repo->reset('HEAD~1', 'soft');
$repo->reset('main', 'hard');

restore

public function restore(string $path, ?string $source = null): void

Restore a file from the index (default) or from a specific commit.

$repo->restore('src/File.php');                // From index
$repo->restore('src/File.php', 'HEAD~1');      // From a commit

cherryPick

public function cherryPick(string $revision): ObjectId

Apply a commit as a new commit on the current branch, preserving the original author.

$newId = $repo->cherryPick('abc123');

revert

public function revert(string $revision): ObjectId

Create a commit that undoes another commit.

$revertId = $repo->revert('abc123');

Status

status

public function status(): array

Compute working tree status. Returns an array of StatusEntry objects, each containing:

  • path: the file path
  • index: FileStatus enum for HEAD-to-index changes
  • worktree: FileStatus enum for index-to-worktree changes
foreach ($repo->status() as $entry) {
    echo "{$entry->index->value}{$entry->worktree->value} {$entry->path}\n";
}

statusPorcelainV2

public function statusPorcelainV2(): string

Machine-readable status output in git's porcelain v2 format.

echo $repo->statusPorcelainV2();
// 1 M. N... 000000 000000 000000 0000...0000 0000...0000 modified-file.txt
// ? untracked-file.txt

Diff

diff

public function diff(?string $pathspec = null): array

Diff working tree against the index (unstaged changes). Returns an array of DiffResult objects. Optionally filter by a single path.

$diffs = $repo->diff();

foreach ($diffs as $diff) {
    echo $diff->format();  // Unified diff output
}

diffStaged

public function diffStaged(?string $pathspec = null): array

Diff index against HEAD (staged changes). Returns DiffResult[].

$staged = $repo->diffStaged();

diffTree

public function diffTree(ObjectId $a, ObjectId $b): array

Diff two trees by their ObjectId. Returns DiffResult[].

$commit1 = $repo->resolve('HEAD~1');
$commit2 = $repo->resolve('HEAD');
$diffs = $repo->diffTree($commit1, $commit2);

Log

log

public function log(int $limit = 50, ?ObjectId $from = null): array

Walk commit history in topological order. Returns Commit[]. If $from is null, starts from HEAD.

$commits = $repo->log(10);

logPath

public function logPath(string $path, int $limit = 50): array

Log filtered by a file path. Returns only commits that touch the given file.

$commits = $repo->logPath('src/Repository.php', 20);

Merge

merge

public function merge(string $branch): MergeResult

Merge a branch into HEAD. Detects fast-forward merges automatically. For non-fast-forward merges, performs tree-level conflict detection and creates a merge commit if clean.

Returns a MergeResult with:

  • clean: whether the merge succeeded without conflicts
  • commitId: the merge commit ID (if clean)
  • conflictPaths: array of conflicting file paths (if not clean)
$result = $repo->merge('feature/login');

if ($result->clean) {
    echo "Merged: {$result->commitId->hex}\n";
} else {
    echo "Conflicts in: " . implode(', ', $result->conflictPaths) . "\n";
}

mergeBase

public function mergeBase(ObjectId $a, ObjectId $b): ?ObjectId

Find the merge base (lowest common ancestor) of two commits. Returns null if no common ancestor exists.

$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);

Network

fetch

public function fetch(string $remote = 'origin'): void

Fetch from a remote. Downloads new objects and updates remote tracking refs.

$repo->fetch();
$repo->fetch('upstream');

push

public function push(string $remote = 'origin', ?string $branch = null): void

Push the current branch (or a specified branch) to a remote.

$repo->push();
$repo->push('origin', 'feature/login');

Worktrees

addWorktree

public function addWorktree(string $path, string $branch, ?ObjectId $from = null, ?string $name = null): Worktree

Add a linked worktree. Creates the branch if it does not exist, sets up the worktree metadata, and materializes the working tree files. Pass $name when you need a deterministic metadata slug that differs from the checkout directory basename.

$wt = $repo->addWorktree('/tmp/review', 'feature/review');
$wt = $repo->addWorktree('/tmp/a/divine-child', 'feature/review', name: 'app-theme');

removeWorktree

public function removeWorktree(string $pathOrName, bool $force = false): void

Remove a linked worktree. Use $force = true to remove even if locked.

worktrees

public function worktrees(): array

List all worktrees (main + linked). Returns Worktree[].

foreach ($repo->worktrees() as $wt) {
    echo "{$wt->path} [{$wt->branch}]\n";
}

Config

config

public function config(): GitConfig

Access the repository's git config. Read and write config values.

$config = $repo->config();
$name = $config->get('user.name');
$config->set('user.email', 'new@example.com');

Internal access

These methods expose internal components for advanced use cases.

objectDatabase

public function objectDatabase(): ObjectDatabase

Access the raw object database (loose + pack stores).

refDatabase

public function refDatabase(): RefDatabase

Access the raw ref database (loose + packed refs).

On this page