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): RepositoryOpen 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): RepositoryInitialize 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): RepositoryClone 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): boolCheck 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): boolCheck 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): ?stringResolve 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(): stringReturns the per-worktree git directory. For regular repos this is .git/. For linked worktrees, this is .git/worktrees/<name>/.
commonGitDir
public function commonGitDir(): stringReturns 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(): stringReturns the working tree root path.
isLinkedWorktree
public function isLinkedWorktree(): boolReturns true if this repository handle is a linked worktree.
Objects
readObject
public function readObject(string $hash): GitObjectRead 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): ObjectIdWrite 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): stringReturn the raw content of an object, equivalent to git cat-file -p.
echo $repo->catFile('a1b2c3d4...');objectExists
public function objectExists(string $hash): boolCheck if an object exists in the repository (loose or packed).
listObjects
public function listObjects(): arrayList all object hashes in the repository. Returns an array of hex strings.
Refs
head
public function head(): CommitReturns 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): ?stringWithout 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 nullbranches
public function branches(): arrayReturns a sorted array of all branch names (without the refs/heads/ prefix).
$branches = $repo->branches();
// ['bugfix/typo', 'feature/login', 'main']tags
public function tags(): arrayReturns a sorted array of all tag names (without the refs/tags/ prefix).
resolve
public function resolve(string $revision): ObjectIdResolve 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(): arrayReturns 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): voidUpdate a ref to point to a new target.
$repo->updateRef('refs/heads/main', $commitId);createBranch
public function createBranch(string $name, ?ObjectId $from = null): voidCreate 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): voidDelete a branch by name.
$repo->deleteBranch('feature/old');createTag
public function createTag(string $name, string $message, ?ObjectId $target = null, ?string $tagger = null): ObjectIdCreate 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(): stringResolve 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): boolCheck 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): voidSwitch branches or detach HEAD. Updates HEAD, the index, and the working tree.
$repo->checkout('feature/login');
$repo->checkout('abc123def456...'); // Detached HEADIndex
index
public function index(): IndexRead and return the current index (staging area) from .git/index.
add
public function add(string ...$paths): voidStage 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): voidUnstage 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): voidMove/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): ObjectIdCreate 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): arrayShow 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'): voidReset 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): voidRestore 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 commitcherryPick
public function cherryPick(string $revision): ObjectIdApply a commit as a new commit on the current branch, preserving the original author.
$newId = $repo->cherryPick('abc123');revert
public function revert(string $revision): ObjectIdCreate a commit that undoes another commit.
$revertId = $repo->revert('abc123');Status
status
public function status(): arrayCompute working tree status. Returns an array of StatusEntry objects, each containing:
path: the file pathindex:FileStatusenum for HEAD-to-index changesworktree:FileStatusenum for index-to-worktree changes
foreach ($repo->status() as $entry) {
echo "{$entry->index->value}{$entry->worktree->value} {$entry->path}\n";
}statusPorcelainV2
public function statusPorcelainV2(): stringMachine-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.txtDiff
diff
public function diff(?string $pathspec = null): arrayDiff 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): arrayDiff index against HEAD (staged changes). Returns DiffResult[].
$staged = $repo->diffStaged();diffTree
public function diffTree(ObjectId $a, ObjectId $b): arrayDiff 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): arrayWalk 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): arrayLog 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): MergeResultMerge 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 conflictscommitId: 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): ?ObjectIdFind 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'): voidFetch 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): voidPush 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): WorktreeAdd 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): voidRemove a linked worktree. Use $force = true to remove even if locked.
worktrees
public function worktrees(): arrayList all worktrees (main + linked). Returns Worktree[].
foreach ($repo->worktrees() as $wt) {
echo "{$wt->path} [{$wt->branch}]\n";
}Config
config
public function config(): GitConfigAccess 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(): ObjectDatabaseAccess the raw object database (loose + pack stores).
refDatabase
public function refDatabase(): RefDatabaseAccess the raw ref database (loose + packed refs).