Saltar al contenido

Lectura de repositorios

Pitmaster lee repositorios git analizando directamente los formatos del disco: objetos sueltos (archivos comprimidos con zlib en .git/objects/), archivos pack (.git/objects/pack/), refs (archivos en .git/refs/ y packed-refs) y el índice (.git/index).

Abrir un repositorio

PHP
use Pitmaster\Pitmaster;

$repo = Pitmaster::open('/path/to/project');

Pitmaster admite tres diseños de repositorio:

  1. Repositorios normales: .git es un directorio en la raíz del proyecto.
  2. Árboles de trabajo vinculados: .git es un archivo que contiene gitdir: <path> y apunta a .git/worktrees/<name>/.
  3. Repositorios bare: la ruta contiene directamente HEAD, objects/, etc.
PHP
// Detection helpers
Pitmaster::isRepository('/path');  // true for any of the above
Pitmaster::isWorktree('/path');    // true only for linked worktrees

Leer objetos

Cada dato de un repositorio git se almacena como un objeto identificado por su hash: SHA-1 (40 caracteres hexadecimales) de forma predeterminada o SHA-256 (64 caracteres hexadecimales) en repositorios creados con Pitmaster::init($path, 'sha256'). Hay cuatro tipos de objeto: blob, árbol, commit y tag.

Los métodos basados en hash siguientes requieren el hash hexadecimal completo; no se aceptan hashes abreviados.

readObject

PHP
use Pitmaster\Object\Blob;
use Pitmaster\Object\Tree;
use Pitmaster\Object\Commit;
use Pitmaster\Object\Tag;

$object = $repo->readObject('a1b2c3d4e5f6...');

match (true) {
    $object instanceof Blob   => handleBlob($object),
    $object instanceof Tree   => handleTree($object),
    $object instanceof Commit => handleCommit($object),
    $object instanceof Tag    => handleTag($object),
};

Los objetos se buscan primero en el almacenamiento suelto y luego en los archivos pack. Las búsquedas en archivos pack usan la tabla de dispersión .idx para la búsqueda binaria O(log n).

catFile

Para recuperar contenido de forma sencilla, sin comprobar el tipo:

PHP
$content = $repo->catFile('a1b2c3d4e5f6...');
echo $content;

objectExists

PHP
if ($repo->objectExists('a1b2c3d4e5f6...')) {
    // Object is in the repository
}

listObjects

PHP
$hashes = $repo->listObjects();
// ['a1b2c3d4...', 'f6e5d4c3...', ...]

Devuelve los hashes hexadecimales de todos los objetos (sueltos y empaquetados).

Trabajar con blobs

Un blob contiene el contenido sin procesar de un archivo.

PHP
$blob = $repo->readObject($hash);
echo $blob->content;     // Raw file bytes
echo $blob->size();      // Content length
echo $blob->id->hex;     // Object hash

Trabajar con árboles

Un árbol es un listado de directorios. Cada entrada tiene un modo, un nombre y un hash.

PHP
$tree = $repo->readObject($hash);

foreach ($tree->entries as $entry) {
    echo "{$entry->mode} {$entry->name} {$entry->hash->hex}\n";
    // 100644 README.md abc123...
    // 40000 src def456...
    // 100755 deploy.sh 789012...

    if ($entry->isTree()) {
        // Recurse into subdirectory (mode 40000)
    } elseif ($entry->isExecutable()) {
        // Executable file (mode 100755)
    } elseif ($entry->isBlob()) {
        // Regular file (any 10xxxx mode, so executables match too)
    } elseif ($entry->isSymlink()) {
        // Symbolic link (mode 120000)
    } elseif ($entry->isGitlink()) {
        // Submodule (mode 160000)
    }
}

Trabajo con commits

Un commit vincula una instantánea del árbol con sus padres y metadatos.

PHP
$commit = $repo->head();

echo $commit->id->hex;      // Full object hash
echo $commit->tree->hex;    // Tree object hash
echo $commit->author;       // "Name <email> timestamp tz"
echo $commit->committer;    // "Name <email> timestamp tz"
echo $commit->message;      // Full commit message

// Parent commits
foreach ($commit->parents as $parentId) {
    $parent = $repo->readObject($parentId->hex);
}

// Merge detection
if ($commit->isMerge()) {
    // Two or more parents
}

Trabajo con etiquetas

Las etiquetas anotadas son objetos con un destino, un tipo, un nombre, un etiquetador y un mensaje.

PHP
$tag = $repo->readObject($hash);

echo $tag->object->hex;           // Target object hash
echo $tag->objectType->value;     // 'commit', 'tree', 'blob', or 'tag'
echo $tag->name;            // Tag name
echo $tag->tagger;          // "Name <email> timestamp tz"
echo $tag->message;         // Tag message

Lectura de referencias

HEAD actual

PHP
$commit = $repo->head();

Resuelve HEAD (siguiendo referencias simbólicas como HEAD -> refs/heads/main -> commit hash) y devuelve el objeto commit.

Rama actual

PHP
$branch = $repo->branch();  // 'main' or null if detached

Listado de ramas y etiquetas

PHP
$branches = $repo->branches();  // ['bugfix/typo', 'feature/login', 'main']
$tags = $repo->tags();          // ['v1.0.0', 'v1.1.0']

Todas las referencias

PHP
$refs = $repo->allRefs();
// [
//     'refs/heads/main' => 'abc123...',
//     'refs/heads/feature' => 'def456...',
//     'refs/tags/v1.0.0' => '789012...',
//     'refs/remotes/origin/main' => 'abc123...',
// ]

Resolución de revisiones

PHP
$id = $repo->resolve('HEAD');
$id = $repo->resolve('main');
$id = $repo->resolve('v1.0.0');
$id = $repo->resolve('HEAD~3');
$id = $repo->resolve('main^2');
$id = $repo->resolve('3f2c1e0b9d6a4f8e7c5b2a1d0e9f8c7b6a5d4e3f');

El analizador de revisiones admite los operadores ~N (ancestro) y ^N (padre), encadenados sin límite. Los hashes deben indicarse completos.

Recorrido del historial de commits

log

Los commits se devuelven primero los más recientes, ordenados por la marca de tiempo del committer.

PHP
// Last 20 commits from HEAD
$commits = $repo->log(20);

foreach ($commits as $commit) {
    $short = substr($commit->id->hex, 0, 7);
    echo "{$short} {$commit->message}\n";
}

log desde un commit específico

PHP
$from = $repo->resolve('feature/login');
$commits = $repo->log(10, $from);

log filtrado por ruta de archivo

PHP
$commits = $repo->logPath('src/Repository.php', 20);

Devuelve solo los commits en los que el archivo especificado cambió (se añadió, modificó, eliminó o renombró) respecto del primer padre. La ruta debe ser la ruta exacta del archivo; los directorios no coinciden. Solo se examinan los 5 * $limit commits más recientes, por lo que podrían faltar coincidencias anteriores (#7).

show

PHP
$result = $repo->show('HEAD');
$commit = $result['commit'];
$diffs = $result['diff'];

echo "Author: {$commit->author}\n";
echo "Message: {$commit->message}\n\n";

foreach ($diffs as $diff) {
    echo $diff->format();
}

Detección de la rama predeterminada

PHP
$default = $repo->defaultBranch();
// Checks: remote HEAD symref -> local HEAD -> main/master fallback

Cuando se configura remote.origin.url y los procesos están habilitados, la primera comprobación envía una solicitud smart HTTP al remoto. Se ignoran los errores de red y luego se realizan las comprobaciones locales.

Ancestría de fusiones

PHP
// Is feature fully merged into main?
$merged = $repo->isBranchMerged('feature/done', 'main');

// Find the merge base of two commits
$base = $repo->mergeBase(
    $repo->resolve('main'),
    $repo->resolve('feature/login'),
);