Escritura en repositorios
Pitmaster escribe repositorios git con los mismos formatos binarios que el binario canónico git. Los objetos se escriben en el almacén de objetos sueltos (comprimidos con zlib), el índice se escribe en formato v2 (o v3 cuando las entradas requieren indicadores extendidos; un índice existente conserva su versión) y las referencias se escriben como archivos sueltos. Los datos escritos se pueden verificar con git fsck --strict.
Inicializar un repositorio
use Pitmaster\Pitmaster;
$repo = Pitmaster::init('/path/to/new-project');
Esto crea:
.git/objects/(almacén de objetos vacío).git/refs/heads/y.git/refs/tags/(directorios de referencias vacíos).git/HEADque contieneref: refs/heads/main.git/hooks/,.git/info/excludey.git/description.git/configcon la configuración predeterminada
Pasa 'sha256' como segundo argumento para crear un repositorio SHA-256: Pitmaster::init($path, 'sha256').
La configuración predeterminada no establece core.logallrefupdates, por lo que Pitmaster no registra entradas de reflog en los repositorios que crea hasta que establezcas esa clave en true (#8). Los repositorios creados por git init ya la tienen configurada.
Preparación de archivos
add
$repo->add('src/Feature.php');
$repo->add('src/Feature.php', 'tests/FeatureTest.php', 'README.md');
Cada argumento es una ruta de archivo relativa a la raíz del árbol de trabajo. Los directorios y patrones glob no se expanden, y si falta un archivo se lanza RuntimeException. Para cada archivo, Pitmaster:
- Lee el contenido del archivo del árbol de trabajo
- Crea un objeto
Bloby calcula su hash - Escribe el blob en
.git/objects/(comprimido con zlib) - Actualiza la entrada del índice para esa ruta con el nuevo hash y los metadatos del archivo
remove
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
Elimina las rutas rastreadas del índice y del árbol de trabajo. Usa --cached para eliminarlas solo del índice, o -r / --recursive para directorios rastreados.
mv
$repo->mv('old-name.php', 'new-name.php');
Renombra el archivo en el árbol de trabajo, elimina la entrada anterior del índice y añade la nueva.
Crear commits
$commitId = $repo->commit('Fix null pointer bug');
echo $commitId->hex;
El proceso de commit:
- Crear el árbol a partir del índice: la lista plana de entradas del índice se organiza en una jerarquía de árboles (los directorios se convierten en objetos árbol y los archivos son referencias a blobs). Cada árbol se escribe en el almacén de objetos.
- Determinar el padre: el commit HEAD actual pasa a ser el padre. Los commits raíz (el primer commit de un repositorio) no tienen padres.
- Determinar el autor: se obtiene de las variables de entorno
PITMASTER_AUTHOR_*yGIT_AUTHOR_*, luego de las constantesPITMASTER_AUTHOR_NAME/PITMASTER_AUTHOR_EMAIL, después de.git/config(user.name/user.email) y, por último, dePitmaster <pitmaster@localhost>. El committer se resuelve de la misma manera. - Escribir el objeto commit: el contenido del commit (
tree,parent,author,committer, mensaje) se serializa, se calcula su hash y se escribe. - Actualizar HEAD: la referencia de rama a la que apunta HEAD se actualiza con el nuevo hash del commit.
Los hooks se ejecutan antes y después de estos pasos (pre-commit, prepare-commit-msg, commit-msg, post-commit), salvo que el manejador se haya abierto con ['hooks' => false]. commit() lanza una excepción si falta el mensaje, si el árbol no ha cambiado respecto de HEAD o si el índice tiene rutas sin fusionar.
Autor personalizado
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
Verificación con git fsck
Las pruebas de integración de Pitmaster verifican los objetos escritos con git fsck --strict, y también puedes verificarlo manualmente:
cd /path/to/repo
git fsck --strict
Si Pitmaster escribe un objeto no válido, git fsck informará del error de inmediato.
Administración de ramas
Crear una rama
// Branch from HEAD
$repo->createBranch('feature/new-thing');
// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
Crea un archivo de referencia en .git/refs/heads/<name> que contiene el hash del commit de destino.
Eliminar una rama
$repo->deleteBranch('feature/old');
Cambiar de rama
$repo->checkout('feature/login');
Actualiza HEAD a una referencia simbólica que apunta a la rama, restablece el índice para que coincida con el árbol de la punta de la rama y actualiza el árbol de trabajo.
Los cambios locales en rutas que difieren entre los dos árboles detienen el checkout con un RuntimeException. Los cambios sin confirmar en los demás archivos rastreados se reemplazan por el contenido de la rama, como haría git con los archivos que conservaría (#2). Confírmalos o guárdalos en stash antes de cambiar de rama.
Comprobar si se ha fusionado
if ($repo->isBranchMerged('feature/done')) {
$repo->deleteBranch('feature/done');
}
Administración de etiquetas
Etiqueta ligera
$repo->createLightweightTag('v1.0.0'); // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));
Una etiqueta ligera es simplemente una referencia que apunta directamente a un commit.
Etiqueta anotada
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
Una etiqueta anotada crea un objeto de etiqueta (con etiquetador, mensaje y destino) y una referencia que apunta a él. El etiquetador es la identidad del committer actual, resuelta igual que la del autor del commit.
Etiquetador personalizado
$tagId = $repo->createTag(
'v2.0.0',
'Release version 2.0.0',
$repo->resolve('HEAD'),
'Jane Smith <jane@example.com> 1700000000 +0000',
);
Actualizar referencias directamente
$repo->updateRef('refs/heads/main', $commitId);
$repo->updateRef('refs/tags/latest', $commitId);
Escribir objetos directamente
Para casos de uso avanzados, puedes escribir objetos sin pasar por la capa de alto nivel API.
Escribir 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!"
En un repositorio SHA-256, pasa el algoritmo: Blob::fromContent('Hello, world!', 'sha256').
Escribir un árbol
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() escribe las entradas en el orden indicado y las cadenas de modo tal como se proporcionan. Pásalas en el orden de árbol de git (por nombre, comparando los directorios como si su nombre terminara en /) y escribe los modos de directorio como 40000: git almacena los modos de árbol sin el cero inicial y git fsck --strict rechaza 040000.
Escribir 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);
Reset
// 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
// 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 y revert
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');
// Undo a commit
$revertId = $repo->revert('HEAD~2');
Ambos aceptan cualquier revisión que admita resolve(). Se admiten hashes completos; los abreviados no.