Zum Inhalt springen

In Repositories schreiben

Pitmaster schreibt git-Repositories in denselben Binärformaten wie die maßgebliche git-Binärdatei. Objekte werden im Speicher für lose Objekte gespeichert (zlib-komprimiert), der Index im Format v2 (oder v3, wenn Einträge erweiterte Flags benötigen; ein vorhandener Index behält seine Version) und Refs als lose Dateien. Mit git fsck --strict lassen sich die geschriebenen Daten überprüfen.

Repository initialisieren

PHP
use Pitmaster\Pitmaster;

$repo = Pitmaster::init('/path/to/new-project');

Dabei werden erstellt:

  • .git/objects/ (leerer Objektspeicher)
  • .git/refs/heads/ und .git/refs/tags/ (leere Ref-Verzeichnisse)
  • .git/HEAD mit ref: refs/heads/main
  • .git/hooks/, .git/info/exclude und .git/description
  • .git/config mit Standardeinstellungen

Übergib 'sha256' als zweites Argument, um ein SHA-256-Repository zu erstellen: Pitmaster::init($path, 'sha256').

In der Standardkonfiguration wird core.logallrefupdates nicht festgelegt. Daher zeichnet Pitmaster in einem von ihm erstellten Repository keine Reflog-Einträge auf, bis du diesen Schlüssel auf true setzt (#8). Bei mit git init erstellten Repositories ist der Schlüssel bereits gesetzt.

Dateien vormerken

add

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

Jedes Argument ist ein Dateipfad relativ zum Stammverzeichnis des Arbeitsverzeichnisses. Verzeichnisse und Glob-Muster werden nicht erweitert; bei einer fehlenden Datei wird RuntimeException ausgelöst. Für jede Datei führt Pitmaster folgende Schritte aus:

  1. Liest den Dateiinhalt aus dem Arbeitsverzeichnis
  2. Erstellt ein Blob-Objekt und berechnet dessen Hash
  3. Schreibt den Blob nach .git/objects/ (zlib-komprimiert)
  4. Aktualisiert den Indexeintrag für diesen Pfad mit dem neuen Hash und den Dateimetadaten

remove

PHP
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');

Entfernt versionierte Pfade aus dem Index und dem Arbeitsverzeichnis. Verwende --cached, um nur den Indexeintrag zu entfernen, oder -r / --recursive für versionierte Verzeichnisse.

mv

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

Benennt die Datei im Arbeitsverzeichnis um, entfernt den alten Indexeintrag und fügt den neuen hinzu.

Commits erstellen

PHP
$commitId = $repo->commit('Fix null pointer bug');
echo $commitId->hex;

Der Commit-Ablauf:

  1. Tree aus dem Index erstellen: Die flache Liste der Indexeinträge wird zu einer Tree-Hierarchie geordnet (Verzeichnisse werden zu Tree-Objekten, Dateien zu Blob-Referenzen). Jeder Tree wird im Objektspeicher abgelegt.
  2. Parent ermitteln: Der aktuelle HEAD-Commit wird zum Parent. Root-Commits (der erste Commit in einem Repository) haben keine Parents.
  3. Autor ermitteln: Die Angaben stammen aus den Umgebungsvariablen PITMASTER_AUTHOR_* und GIT_AUTHOR_*, dann aus den Konstanten PITMASTER_AUTHOR_NAME/PITMASTER_AUTHOR_EMAIL, anschließend aus .git/config (user.name/user.email) und zuletzt aus Pitmaster <pitmaster@localhost>. Der Committer wird auf dieselbe Weise ermittelt.
  4. Commit-Objekt schreiben: Der Commit-Inhalt (tree, parent, author, committer, Nachricht) wird serialisiert, gehasht und geschrieben.
  5. HEAD aktualisieren: Die Branch-Ref, auf die HEAD verweist, wird auf den Hash des neuen Commits gesetzt.

Vor und nach diesen Schritten werden Hooks ausgeführt (pre-commit, prepare-commit-msg, commit-msg, post-commit), sofern das Handle nicht mit ['hooks' => false] geöffnet wurde. commit() löst einen Fehler aus, wenn die Nachricht fehlt, der Tree gegenüber HEAD unverändert ist oder der Index nicht zusammengeführte Pfade enthält.

Benutzerdefinierter Autor

PHP
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);

Mit git fsck überprüfen

Die Integrationstests von Pitmaster prüfen geschriebene Objekte anhand von git fsck --strict. Dieselbe Prüfung kannst du manuell ausführen:

Shell
cd /path/to/repo
git fsck --strict

Wenn Pitmaster ein ungültiges Objekt schreibt, meldet git fsck den Fehler sofort.

Branches verwalten

Branch erstellen

PHP
// Branch from HEAD
$repo->createBranch('feature/new-thing');

// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));

Erstellt unter .git/refs/heads/<name> eine Ref-Datei mit dem Hash des Ziel-Commits.

Branch löschen

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

Branches wechseln

PHP
$repo->checkout('feature/login');

Aktualisiert HEAD zu einer symbolischen Ref, die auf den Branch verweist, setzt den Index passend zum Tree der Branch-Spitze zurück und aktualisiert das Arbeitsverzeichnis.

Lokale Änderungen an Pfaden, die sich zwischen den beiden Trees unterscheiden, brechen den Checkout mit einem RuntimeException ab. Nicht committete Änderungen an allen anderen versionierten Dateien werden durch den Inhalt des Branches ersetzt, sofern git sie beibehalten würde (#2). Committe oder sichere diese Änderungen vor dem Wechsel.

Prüfen, ob zusammengeführt

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

Tags verwalten

Lightweight-Tag

PHP
$repo->createLightweightTag('v1.0.0');                          // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));

Ein Lightweight-Tag ist lediglich eine Ref, die direkt auf einen Commit verweist.

Annotiertes Tag

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

Ein annotiertes Tag erstellt ein Tag-Objekt (mit Tagger, Nachricht und Ziel) sowie eine darauf verweisende Ref. Als Tagger wird die Identität des aktuellen Committers verwendet, die wie der Commit-Autor ermittelt wird.

Benutzerdefinierter Tagger

PHP
$tagId = $repo->createTag(
    'v2.0.0',
    'Release version 2.0.0',
    $repo->resolve('HEAD'),
    'Jane Smith <jane@example.com> 1700000000 +0000',
);

Refs direkt aktualisieren

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

Objekte direkt schreiben

Für fortgeschrittene Anwendungsfälle kannst du Objekte schreiben, ohne den übergeordneten API zu verwenden.

Blob schreiben

PHP
use Pitmaster\Object\Blob;

$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
echo $id->hex;  // SHA-1 of "blob 13\0Hello, world!"

Übergib in einem SHA-256-Repository den Algorithmus: Blob::fromContent('Hello, world!', 'sha256').

Tree schreiben

PHP
use Pitmaster\Object\Tree;
use Pitmaster\Object\TreeEntry;
use Pitmaster\Object\ObjectId;

$entries = [
    new TreeEntry('100644', 'README.md', $blobId),
    new TreeEntry('40000', 'src', $subtreeId),
];

$tree = Tree::fromEntries($entries);
$id = $repo->writeObject($tree);

Tree::fromEntries() schreibt die Einträge in der angegebenen Reihenfolge und verwendet die angegebenen Moduszeichenfolgen. Übergib sie in der Tree-Reihenfolge von git (nach Namen sortiert, wobei Verzeichnisse so verglichen werden, als endete ihr Name mit /) und gib Verzeichnismodi als 40000 an: git speichert Tree-Modi ohne die führende Null, und git fsck --strict lehnt 040000 ab.

Commit schreiben

PHP
use Pitmaster\Object\Commit;
use Pitmaster\Object\ObjectId;
use Pitmaster\Object\ObjectType;

$content = Commit::buildContent(
    tree: $treeId,
    parents: [$parentId],
    author: 'Name <email> 1700000000 +0000',
    committer: 'Name <email> 1700000000 +0000',
    message: "Commit message\n",
);

$commitId = ObjectId::compute(ObjectType::Commit, $content);
$commit = Commit::parse($content, $commitId);
$repo->writeObject($commit);

Reset

PHP
// Soft: move HEAD only
$repo->reset('HEAD~1', 'soft');

// Mixed: move HEAD + reset index (default)
$repo->reset('HEAD~1');

// Hard: move HEAD + reset index + reset working tree
$repo->reset('HEAD~3', 'hard');

Restore

PHP
// Restore a file from the index into the worktree
$repo->restore('src/File.php');

// Restore the index version from HEAD (the worktree is left alone)
$repo->restore('src/File.php', staged: true);

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

// Restore both index and worktree from a specific commit
$repo->restore('src/File.php', 'HEAD~1', staged: true, worktree: true);

Cherry-pick und Revert

PHP
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');

// Undo a commit
$revertId = $repo->revert('HEAD~2');

Beide akzeptieren jede Revision, die resolve() akzeptiert. Vollständige Hashes funktionieren; abgekürzte Hashes nicht.