
# 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.

```
.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

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
```

```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`.

```
# 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/` :
```
.git/logs/HEAD
.git/logs/refs/heads/main
.git/logs/refs/stash
```

Chaque ligne consigne :
```
<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');
```
