Aller au contenu

Références

Les références (refs) sont des pointeurs nommés vers des commits. Les branches, les tags, HEAD et les références de suivi distant sont tous des refs. Pitmaster lit les refs depuis des fichiers libres, packed-refs et reftable, et écrit dans des fichiers libres et packed-refs.

RefDatabase

RefDatabase est la couche composite qui recherche d'abord les refs libres, puis reftable (si le dépôt en possède un), puis packed-refs.

PHP
use Pitmaster\Ref\RefDatabase;

$refs = new RefDatabase($gitDir, $commonDir);

// Resolve a ref to an ObjectId
$id = $refs->resolve('refs/heads/main');

// Resolve HEAD (follows symbolic refs)
$headId = $refs->resolveHead();

// Read HEAD as a symbolic ref (null when HEAD is detached)
$symref = $refs->readHead();
echo $symref?->target;  // 'refs/heads/main'

// List all refs
foreach ($refs->list() as $name => $id) {
    echo "{$id->hex} {$name}\n";
}

// Update a ref
$refs->update('refs/heads/main', $newCommitId);

// Delete a ref
$refs->delete('refs/heads/old-branch');

Pour les worktrees liés, RefDatabase conserve HEAD et les autres pseudo-refs propres au worktree (ORIG_HEAD, MERGE_HEAD, CHERRY_PICK_HEAD, REVERT_HEAD, REBASE_HEAD, BISECT_HEAD) dans le répertoire git propre au worktree. Les branches, les tags et les autres entrées refs/ sont écrits dans le répertoire commun, et packed-refs est toujours lu depuis ce répertoire.

Références libres

Les refs libres sont des fichiers individuels dans .git/refs/. Chaque fichier contient le hachage hexadécimal de l'objet cible (40 caractères, ou 64 pour SHA-256), suivi d'un saut de ligne.

Code
.git/refs/heads/main           -> abc123def456...\n
.git/refs/tags/v1.0.0          -> 789012abc345...\n
.git/refs/remotes/origin/main  -> abc123def456...\n
PHP
use Pitmaster\Ref\LooseRefStore;

$store = new LooseRefStore($gitDir, $commonDir); // $commonDir defaults to $gitDir

$id = $store->resolve('refs/heads/main');
$store->update('refs/heads/main', $commitId);
$store->delete('refs/heads/old');

HEAD est une ref spéciale qui peut être :

Symbolique : pointe vers une autre ref (le cas le plus courant, lorsque vous êtes sur une branche) :

Code
ref: refs/heads/main\n

Détachée : pointe directement vers un hachage de commit :

Code
abc123def456789012abc345def678901234abcd\n
PHP
use Pitmaster\Ref\SymbolicRef;

$symref = SymbolicRef::parse('HEAD', $headContent);

if ($symref !== null) {
    echo "On branch: {$symref->target}\n";
    // 'refs/heads/main'
} else {
    echo "Detached HEAD\n";
}

Références packées

Le fichier packed-refs regroupe de nombreuses refs dans un seul fichier pour plus d'efficacité. Git le crée lors de git pack-refs ou de git gc.

Code
# pack-refs with: peeled fully-peeled sorted
abc123def456789012abc345def678901234abcd refs/heads/main
789012abc345def678901234abcdabc123def456 refs/tags/v1.0.0
^def456789012abc345def678901234abcdabc12

Les lignes commençant par ^ sont des valeurs de déréférencement : le commit vers lequel pointe finalement un tag annoté.

PHP
use Pitmaster\Ref\PackedRefStore;

$store = new PackedRefStore($commonDir);

$id = $store->resolve('refs/heads/main');

foreach ($store->list() as $name => $id) {
    echo "{$id->hex} {$name}\n";
}

Valeurs déréférencées

Pour les tags annotés, le fichier packed-refs peut inclure la valeur déréférencée sur la ligne qui suit la ref du tag. Il s'agit du commit vers lequel l'objet tag pointe finalement, après avoir éventuellement traversé plusieurs objets tag.

PHP
$peeled = $store->peeled('refs/tags/v1.0.0');
// Returns the commit ObjectId, or null if not peeled

Ordre de résolution des références

Lorsqu'il résout une ref, Pitmaster vérifie dans cet ordre :

  1. Ref libre (fichier dans .git/refs/)
  2. Reftable, si .git/reftable/tables.list existe
  3. Ref packée (entrée dans .git/packed-refs)

Les refs libres sont prioritaires. Lorsqu'une ref packée est mise à jour, la nouvelle valeur est écrite sous forme de ref libre (le fichier packed-refs n'est pas réécrit). La suppression d'une ref efface à la fois le fichier libre et l'entrée packée. $repo->packRefs() réécrit packed-refs à partir des refs actuelles et supprime les fichiers libres.

SymbolicRef

Les refs symboliques sont des références qui pointent vers d'autres références au lieu de pointer directement vers des objets.

PHP
use Pitmaster\Ref\SymbolicRef;

// Parse
$ref = SymbolicRef::parse('HEAD', "ref: refs/heads/main\n");
echo $ref->name;    // 'HEAD'
echo $ref->target;  // 'refs/heads/main'

// Update
$refs->updateSymbolic('HEAD', 'refs/heads/feature');

Reflog

Le reflog enregistre les changements de refs, ce qui permet à git reflog d'afficher l'historique de HEAD et des pointes de branche.

Les fichiers Reflog se trouvent dans .git/logs/ :

Code
.git/logs/HEAD
.git/logs/refs/heads/main
.git/logs/refs/stash

Chaque ligne consigne :

Code
<old-hash> <new-hash> <author> <timestamp> <tz>\t<message>
PHP
use Pitmaster\Ref\Reflog;

$reflog = Reflog::open($gitDir, 'HEAD');

// Read entries
$entries = $reflog->entries();
foreach ($entries as $entry) {
    echo "{$entry['old']} -> {$entry['new']}: {$entry['message']}\n";
}

// Append an entry
$reflog->append(
    $oldId,
    $newId,
    'Name <email> 1700000000 +0000',
    'commit: Add feature',
);

La stash utilise le reflog de refs/stash comme pile de stockage.

Reftable

Reftable est un format binaire plus récent de stockage des refs, conçu pour offrir de meilleures performances avec un grand nombre de refs (des milliers de branches). Il remplace à la fois les refs libres et packed-refs.

Les fichiers Reftable sont stockés dans .git/reftable/ et contiennent :

  • Des enregistrements de refs (triés par nom)
  • Des enregistrements de journal (entrées de reflog)
  • Une indexation par blocs pour accélérer les recherches

Pitmaster lit les enregistrements de refs des fichiers reftable lorsqu'ils sont présents ; les enregistrements de journal ne sont pas lus. Lorsqu'une reftable existe, elle remplace packed-refs pour l'affichage des références. Les mises à jour des refs sont toujours écrites dans des fichiers libres, que git ne lit pas dans un dépôt utilisant reftable ; les dépôts reftable doivent donc être considérés comme étant en lecture seule.

Conventions de nommage des références

Modèle Signification
refs/heads/<name> Branche locale
refs/tags/<name> Tag (léger ou annoté)
refs/remotes/<remote>/<name> Branche de suivi distant
refs/stash Pile de stash
refs/notes/<namespace> Notes Git
HEAD Révision actuellement extraite (symbolique ou détachée)
FETCH_HEAD Résultat du dernier fetch (écrit par git ; le fetch() de Pitmaster ne l'écrit pas)
MERGE_HEAD Branche en cours de fusion
ORIG_HEAD HEAD précédente (avant des opérations risquées)

Utiliser les références avec Repository

PHP
// Read
$branch = $repo->branch();          // Current branch name
$branches = $repo->branches();      // All branch names
$tags = $repo->tags();              // All tag names
$refs = $repo->allRefs();           // All refs as name => hash

// Write
$repo->createBranch('feature');     // Create refs/heads/feature
$repo->deleteBranch('feature');     // Delete refs/heads/feature
$repo->createTag('v1.0', 'Release');// Create annotated tag
$repo->createLightweightTag('v1.0-lite'); // Create lightweight tag
$repo->updateRef('refs/heads/main', $id); // Direct ref update

// Resolve
$id = $repo->resolve('HEAD');
$id = $repo->resolve('main');
$id = $repo->resolve('v1.0.0');
$id = $repo->resolve('HEAD~3');