Écriture dans les dépôts
Pitmaster écrit dans les dépôts git en utilisant les mêmes formats binaires que le binaire canonique git. Les objets sont écrits dans le magasin d’objets libres (compressés avec zlib), l’index est écrit au format v2 (ou v3 lorsque les entrées nécessitent des indicateurs étendus ; un index existant conserve sa version), et les références sont écrites dans des fichiers libres. Les données écrites peuvent être vérifiées avec git fsck --strict.
Initialiser un dépôt
use Pitmaster\Pitmaster;
$repo = Pitmaster::init('/path/to/new-project');
Cela crée :
.git/objects/(magasin d’objets vide).git/refs/heads/et.git/refs/tags/(répertoires de références vides).git/HEADcontenantref: refs/heads/main.git/hooks/,.git/info/excludeet.git/description.git/configavec les paramètres par défaut
Passez 'sha256' comme deuxième argument pour créer un dépôt SHA-256 : Pitmaster::init($path, 'sha256').
La configuration par défaut ne définit pas core.logallrefupdates ; Pitmaster n’enregistre donc aucune entrée de reflog dans un dépôt qu’il a créé tant que vous ne définissez pas cette clé sur true (#8). Les dépôts créés par git init la définissent déjà.
Ajouter des fichiers à l’index
add
$repo->add('src/Feature.php');
$repo->add('src/Feature.php', 'tests/FeatureTest.php', 'README.md');
Chaque argument est un chemin de fichier relatif à la racine de l’arbre de travail. Les répertoires et les motifs glob ne sont pas développés, et un fichier manquant déclenche RuntimeException. Pour chaque fichier, Pitmaster :
- Lit le contenu du fichier dans l’arbre de travail
- Crée un objet
Blobet calcule son hachage - Écrit le blob dans
.git/objects/(compressé avec zlib) - Met à jour l’entrée d’index de ce chemin avec le nouveau hachage et les métadonnées du fichier
remove
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
Supprime les chemins suivis de l’index et de l’arbre de travail. Utilisez --cached pour supprimer uniquement de l’index, ou -r / --recursive pour les répertoires suivis.
mv
$repo->mv('old-name.php', 'new-name.php');
Renomme le fichier dans l’arbre de travail, supprime l’ancienne entrée d’index et ajoute la nouvelle.
Créer des commits
$commitId = $repo->commit('Fix null pointer bug');
echo $commitId->hex;
Étapes de création d’un commit :
- Créer l’arbre à partir de l’index : la liste à plat des entrées d’index est organisée en une hiérarchie d’arbres (les répertoires deviennent des objets arbre, les fichiers des références de blob). Chaque arbre est écrit dans le magasin d’objets.
- Déterminer le parent : le commit HEAD actuel devient le parent. Les commits racine (premier commit d’un dépôt) n’ont pas de parent.
- Déterminer l’auteur : l’auteur est défini par les variables d’environnement
PITMASTER_AUTHOR_*etGIT_AUTHOR_*, puis par les constantesPITMASTER_AUTHOR_NAME/PITMASTER_AUTHOR_EMAIL, puis par.git/config(user.name/user.email), et enfin parPitmaster <pitmaster@localhost>. Le committer est déterminé de la même manière. - Écrire l’objet commit : le contenu du commit (
tree,parent,author,committer, message) est sérialisé, haché et écrit. - Mettre à jour HEAD : la référence de branche vers laquelle pointe HEAD est mise à jour avec le nouveau hachage du commit.
Les hooks s’exécutent autour de ces étapes (pre-commit, prepare-commit-msg, commit-msg, post-commit), sauf si le handle a été ouvert avec ['hooks' => false]. commit() déclenche une erreur si le message est manquant, si l’arbre est inchangé par rapport à HEAD ou si l’index contient des chemins non fusionnés.
Auteur personnalisé
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
Vérifier avec git fsck
La couverture d’intégration de Pitmaster vérifie les objets écrits par rapport à git fsck --strict, et vous pouvez effectuer la même vérification manuellement :
cd /path/to/repo
git fsck --strict
Si Pitmaster écrit un objet invalide, git fsck signalera immédiatement l’erreur.
Gérer les branches
Créer une branche
// Branch from HEAD
$repo->createBranch('feature/new-thing');
// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
Crée un fichier de référence à l’emplacement .git/refs/heads/<name> contenant le hachage du commit cible.
Supprimer une branche
$repo->deleteBranch('feature/old');
Changer de branche
$repo->checkout('feature/login');
Met à jour HEAD pour qu’il pointe vers une référence symbolique vers la branche, réinitialise l’index pour qu’il corresponde à l’arbre de la pointe de la branche, puis met à jour l’arbre de travail.
Les modifications locales dans les chemins qui diffèrent entre les deux arbres interrompent le checkout avec une RuntimeException. Les modifications non validées dans tous les autres fichiers suivis sont remplacées par le contenu de la branche, là où git les conserverait (#2). Validez-les ou mettez-les de côté avant de changer de branche.
Vérifier si une branche a été fusionnée
if ($repo->isBranchMerged('feature/done')) {
$repo->deleteBranch('feature/done');
}
Gérer les tags
Tag léger
$repo->createLightweightTag('v1.0.0'); // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));
Un tag léger est simplement une référence qui pointe directement vers un commit.
Tag annoté
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
Un tag annoté crée un objet tag (avec un tagueur, un message et une cible) ainsi qu’une référence vers celui-ci. Le tagueur correspond à l’identité du committer actuel, déterminée comme pour l’auteur du commit.
Tagueur personnalisé
$tagId = $repo->createTag(
'v2.0.0',
'Release version 2.0.0',
$repo->resolve('HEAD'),
'Jane Smith <jane@example.com> 1700000000 +0000',
);
Mettre à jour directement les références
$repo->updateRef('refs/heads/main', $commitId);
$repo->updateRef('refs/tags/latest', $commitId);
Écrire directement des objets
Pour les cas d’utilisation avancés, vous pouvez écrire des objets sans passer par l’API de haut niveau.
Écrire un blob
use Pitmaster\Object\Blob;
$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
echo $id->hex; // SHA-1 of "blob 13\0Hello, world!"
Dans un dépôt SHA-256, passez l’algorithme : Blob::fromContent('Hello, world!', 'sha256').
Écrire un arbre
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() écrit les entrées dans l’ordre fourni et conserve les chaînes de mode indiquées. Passez-les dans l’ordre des arbres git (par nom, en considérant que le nom des répertoires se termine par /) et écrivez les modes des répertoires sous la forme 40000 : git stocke les modes d’arbre sans le zéro initial, et git fsck --strict rejette 040000.
Écrire un commit
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);
Réinitialiser
// 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');
Restaurer
// 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);
Sélectionner un commit et annuler
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');
// Undo a commit
$revertId = $repo->revert('HEAD~2');
Les deux acceptent toute révision prise en charge par resolve(). Les hachages complets fonctionnent, mais pas les hachages abrégés.