
# Écriture dans les dépôts

Pitmaster écrit dans les dépôts git en utilisant les mêmes formats binaires que le binaire canonique `git`. Les objets sont écrits dans le magasin d’objets libres (compressés avec zlib), l’index est écrit au format v2 (ou v3 lorsque les entrées nécessitent des indicateurs étendus ; un index existant conserve sa version), et les références sont écrites dans des fichiers libres. Les données écrites peuvent être vérifiées avec `git fsck --strict`.

## Initialiser un dépôt

```php
use Pitmaster\Pitmaster;

$repo = Pitmaster::init('/path/to/new-project');
```

Cela crée :
- `.git/objects/` (magasin d’objets vide)
- `.git/refs/heads/` et `.git/refs/tags/` (répertoires de références vides)
- `.git/HEAD` contenant `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` et `.git/description`
- `.git/config` avec les paramètres par défaut

Passez `'sha256'` comme deuxième argument pour créer un dépôt SHA-256 : `Pitmaster::init($path, 'sha256')`.

La configuration par défaut ne définit pas `core.logallrefupdates` ; Pitmaster n’enregistre donc aucune entrée de reflog dans un dépôt qu’il a créé tant que vous ne définissez pas cette clé sur `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Les dépôts créés par `git init` la définissent déjà.

## Ajouter des fichiers à l’index

### add

```php
$repo->add('src/Feature.php');
$repo->add('src/Feature.php', 'tests/FeatureTest.php', 'README.md');
```

Chaque argument est un chemin de fichier relatif à la racine de l’arbre de travail. Les répertoires et les motifs glob ne sont pas développés, et un fichier manquant déclenche `RuntimeException`. Pour chaque fichier, Pitmaster :
1. Lit le contenu du fichier dans l’arbre de travail
2. Crée un objet `Blob` et calcule son hachage
3. Écrit le blob dans `.git/objects/` (compressé avec zlib)
4. Met à jour l’entrée d’index de ce chemin avec le nouveau hachage et les métadonnées du fichier

### remove

```php
$repo->remove('old-file.txt');
$repo->remove('-r', 'src');
$repo->remove('--cached', 'old-file.txt');
```

Supprime les chemins suivis de l’index et de l’arbre de travail. Utilisez `--cached` pour supprimer uniquement de l’index, ou `-r` / `--recursive` pour les répertoires suivis.

### mv

```php
$repo->mv('old-name.php', 'new-name.php');
```

Renomme le fichier dans l’arbre de travail, supprime l’ancienne entrée d’index et ajoute la nouvelle.

## Créer des commits

```php
$commitId = $repo->commit('Fix null pointer bug');
echo $commitId->hex;
```

Étapes de création d’un commit :

1. **Créer l’arbre à partir de l’index** : la liste à plat des entrées d’index est organisée en une hiérarchie d’arbres (les répertoires deviennent des objets arbre, les fichiers des références de blob). Chaque arbre est écrit dans le magasin d’objets.
2. **Déterminer le parent** : le commit HEAD actuel devient le parent. Les commits racine (premier commit d’un dépôt) n’ont pas de parent.
3. **Déterminer l’auteur** : l’auteur est défini par les variables d’environnement `PITMASTER_AUTHOR_*` et `GIT_AUTHOR_*`, puis par les constantes `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, puis par `.git/config` (`user.name`/`user.email`), et enfin par `Pitmaster <pitmaster@localhost>`. Le committer est déterminé de la même manière.
4. **Écrire l’objet commit** : le contenu du commit (`tree`, `parent`, `author`, `committer`, message) est sérialisé, haché et écrit.
5. **Mettre à jour HEAD** : la référence de branche vers laquelle pointe HEAD est mise à jour avec le nouveau hachage du commit.

Les hooks s’exécutent autour de ces étapes (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), sauf si le handle a été ouvert avec `['hooks' => false]`. `commit()` déclenche une erreur si le message est manquant, si l’arbre est inchangé par rapport à HEAD ou si l’index contient des chemins non fusionnés.

### Auteur personnalisé

```php
$author = 'Jane Smith <jane@example.com> 1700000000 +0000';
$commitId = $repo->commit('Add feature', $author);
```

## Vérifier avec git fsck

La couverture d’intégration de Pitmaster vérifie les objets écrits par rapport à `git fsck --strict`, et vous pouvez effectuer la même vérification manuellement :

```bash
cd /path/to/repo
git fsck --strict
```

Si Pitmaster écrit un objet invalide, `git fsck` signalera immédiatement l’erreur.

## Gérer les branches

### Créer une branche

```php
// Branch from HEAD
$repo->createBranch('feature/new-thing');

// Branch from a specific commit
$repo->createBranch('hotfix', $repo->resolve('v1.0.0'));
```

Crée un fichier de référence à l’emplacement `.git/refs/heads/<name>` contenant le hachage du commit cible.

### Supprimer une branche

```php
$repo->deleteBranch('feature/old');
```

### Changer de branche

```php
$repo->checkout('feature/login');
```

Met à jour HEAD pour qu’il pointe vers une référence symbolique vers la branche, réinitialise l’index pour qu’il corresponde à l’arbre de la pointe de la branche, puis met à jour l’arbre de travail.

Les modifications locales dans les chemins qui diffèrent entre les deux arbres interrompent le checkout avec une `RuntimeException`. Les modifications non validées dans tous les autres fichiers suivis sont remplacées par le contenu de la branche, là où git les conserverait ([#2](https://github.com/inline0/pitmaster/issues/2)). Validez-les ou mettez-les de côté avant de changer de branche.

### Vérifier si une branche a été fusionnée

```php
if ($repo->isBranchMerged('feature/done')) {
    $repo->deleteBranch('feature/done');
}
```

## Gérer les tags

### Tag léger

```php
$repo->createLightweightTag('v1.0.0');                          // At HEAD
$repo->createLightweightTag('v0.9.0', $repo->resolve('HEAD~5'));
```

Un tag léger est simplement une référence qui pointe directement vers un commit.

### Tag annoté

```php
$tagId = $repo->createTag('v2.0.0', 'Release version 2.0.0');
```

Un tag annoté crée un objet tag (avec un tagueur, un message et une cible) ainsi qu’une référence vers celui-ci. Le tagueur correspond à l’identité du committer actuel, déterminée comme pour l’auteur du commit.

### Tagueur personnalisé

```php
$tagId = $repo->createTag(
    'v2.0.0',
    'Release version 2.0.0',
    $repo->resolve('HEAD'),
    'Jane Smith <jane@example.com> 1700000000 +0000',
);
```

## Mettre à jour directement les références

```php
$repo->updateRef('refs/heads/main', $commitId);
$repo->updateRef('refs/tags/latest', $commitId);
```

## Écrire directement des objets

Pour les cas d’utilisation avancés, vous pouvez écrire des objets sans passer par l’API de haut niveau.

### Écrire un blob

```php
use Pitmaster\Object\Blob;

$blob = Blob::fromContent('Hello, world!');
$id = $repo->writeObject($blob);
echo $id->hex;  // SHA-1 of "blob 13\0Hello, world!"
```

Dans un dépôt SHA-256, passez l’algorithme : `Blob::fromContent('Hello, world!', 'sha256')`.

### Écrire un arbre

```php
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()` écrit les entrées dans l’ordre fourni et conserve les chaînes de mode indiquées. Passez-les dans l’ordre des arbres git (par nom, en considérant que le nom des répertoires se termine par `/`) et écrivez les modes des répertoires sous la forme `40000` : git stocke les modes d’arbre sans le zéro initial, et `git fsck --strict` rejette `040000`.

### Écrire un commit

```php
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);
```

## Réinitialiser

```php
// 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');
```

## Restaurer

```php
// 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);
```

## Sélectionner un commit et annuler

```php
// Apply a commit on the current branch
$newId = $repo->cherryPick('feature~1');

// Undo a commit
$revertId = $repo->revert('HEAD~2');
```

Les deux acceptent toute révision prise en charge par `resolve()`. Les hachages complets fonctionnent, mais pas les hachages abrégés.
