Pure PHP Git implementation
Pitmaster reads and writes Git repositories in pure PHP. Core repository operations do not shell out to the git binary or rely on FFI. Objects, refs, pack files, the index, and smart HTTP transport are handled natively in PHP.
The problem: PHP applications that need to interact with Git repositories either shell out to the git binary (requires exec(), hard to deploy, security surface) or use FFI/extension bindings (complex setup, version coupling). There's no way to read a pack file, create a commit, or diff two trees from pure PHP.
Pitmaster solves this by implementing the Git object model, binary formats, and protocols natively:
- Read and write loose objects (blob, tree, commit, tag)
- Read and write pack files with full delta chain resolution
- Read and write the index (staging area)
- Compute diffs (Myers O(ND) algorithm, byte-exact with
git diff) - Three-way merge with conflict markers
- Walk commit graphs, compute merge bases
- Speak the Git smart HTTP protocol (clone, fetch, push)
composer require pitmaster/pitmasteruse Pitmaster\Pitmaster;
// Open an existing repository
$repo = Pitmaster::open('/path/to/project');
// Read
$head = $repo->head(); // Current HEAD commit
$log = $repo->log(10); // Last 10 commits
$refs = $repo->allRefs(); // All branches and tags
$obj = $repo->readObject($hash); // Any object by hash
// Write
$repo->add('src/main.php'); // Stage a file
$repo->commit("Fix the bug\n"); // Create a commit
$repo->createBranch('feature'); // Create a branch
$repo->merge('feature'); // Merge a branch
// Diff
$diffs = $repo->diff(); // Unstaged changes
$diffs = $repo->diffStaged(); // Staged changes
$diffs = $repo->diffTree($treeA, $treeB); // Tree-to-tree
// Status
$status = $repo->status(); // WorkingTreeStatus
foreach ($status as $entry) {
echo $entry->shortFormat() . "\n"; // "M src/main.php"
}
// Network
$repo->fetch('origin'); // Fetch from remote
$repo->push('origin', 'main'); // Push to remote
// Init and clone
$repo = Pitmaster::init('/path/to/new');
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/path');Pitmaster ships with a CLI that mirrors a subset of git commands:
./vendor/bin/pitmaster log
./vendor/bin/pitmaster status
./vendor/bin/pitmaster diff
./vendor/bin/pitmaster show HEAD
./vendor/bin/pitmaster add file.txt
./vendor/bin/pitmaster commit -m "message"
./vendor/bin/pitmaster branch feature
./vendor/bin/pitmaster checkout feature
./vendor/bin/pitmaster merge feature
./vendor/bin/pitmaster stash push
./vendor/bin/pitmaster blame file.txt
./vendor/bin/pitmaster grep "pattern"
./vendor/bin/pitmaster tag v1.0 -m "Release"
./vendor/bin/pitmaster reset --hard HEAD~1
./vendor/bin/pitmaster refs
./vendor/bin/pitmaster initPitmaster is exercised with unit tests, integration tests against the canonical git binary, and imported oracle-style scenarios from upstream Git implementations.
# Unit tests (no git binary needed)
composer test:unit
# Integration tests (verified against git)
composer test:integration
# All tests
composer test
# Static analysis
composer analyse
# Coding standards
composer csThe current suite covers unit behavior, end-to-end repository operations, and round-trip comparisons against git. Run composer test for the current totals in this checkout.
- PHP 8.2+
ext-zlib(built-in)ext-mbstring(built-in)ext-json(built-in)
Core repository operations do not require the git binary or FFI. Optional features have extra runtime expectations: hook execution uses proc_open(), and SSH transport requires ext-ssh2.
See SUPPORT_MATRIX.md for the full feature list. Highlights:
| Category | Features |
|---|---|
| Objects | Blob, tree, commit, tag (SHA-1 + SHA-256) |
| Storage | Loose objects, pack files (v1/v2 index, OFS/REF delta), MIDX, commit-graph |
| Index | v2/v3/v4 with extensions (TREE, REUC, FSMN) |
| Refs | Loose, packed, symbolic, reftable, reflog |
| Diff | Myers O(ND), patience, histogram, word diff, rename detection |
| Merge | Three-way, recursive, ours, octopus, fast-forward, conflict markers |
| Network | Smart HTTP (v1/v2), SSH, git://, dumb HTTP, bundles |
| Operations | add, commit, status, diff, merge, checkout, reset, stash, cherry-pick, revert, rebase, blame, grep, bisect, notes |
| Advanced | Submodules, worktrees, sparse checkout, hooks, LFS, rerere, fsmonitor |
src/
├── Pitmaster.php # Static facade (open, init, clone)
├── Repository.php # All operations
├── Object/ # Blob, Tree, Commit, Tag, ObjectId
├── Storage/ # LooseObjectStore, PackFileStore, ObjectDatabase
├── Pack/ # PackFile, PackIndex, DeltaApplier, PackWriter
├── Index/ # Index reader/writer
├── Ref/ # LooseRefStore, PackedRefStore, RefDatabase
├── Diff/ # MyersDiff (O(ND)), TreeDiff, DiffResult
├── Merge/ # ThreeWayMerge, MergeBase, ConflictMarker
├── Graph/ # CommitWalker, Blame, Grep, Bisect, Rebase
├── Status/ # WorkingTreeStatus, GitIgnore, Fsmonitor
├── Protocol/ # SmartHttpClient, PktLine, UploadPackClient
├── Stash/ # Stash (push/pop/apply/list/drop)
├── Config/ # GitConfig, GitAttributes
├── Encoding/ # BinaryReader, VarInt, Leb128
├── Hooks/ # HookRunner
├── Lfs/ # LfsClient, LfsPointer
├── Submodule/ # SubmoduleManager
├── Worktree/ # WorktreeManager
├── Checkout/ # SparseCheckout
└── Exceptions/ # Typed exceptions
MIT