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.
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.
.git/refs/heads/main -> abc123def456...\n
.git/refs/tags/v1.0.0 -> 789012abc345...\n
.git/refs/remotes/origin/main -> abc123def456...\n
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
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) :
ref: refs/heads/main\n
Détachée : pointe directement vers un hachage de commit :
abc123def456789012abc345def678901234abcd\n
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.
# 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é.
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.
$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 :
- Ref libre (fichier dans
.git/refs/) - Reftable, si
.git/reftable/tables.listexiste - 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.
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/ :
.git/logs/HEAD
.git/logs/refs/heads/main
.git/logs/refs/stash
Chaque ligne consigne :
<old-hash> <new-hash> <author> <timestamp> <tz>\t<message>
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
// 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');