
# Scrittura nei repository

Pitmaster scrive nei repository git usando gli stessi formati binari del binario canonico `git`. Gli oggetti vengono scritti nell'archivio di oggetti loose (compressi con zlib), l'indice viene scritto nel formato v2 (oppure v3 quando le voci richiedono flag estesi; un indice esistente mantiene la propria versione) e i riferimenti vengono scritti come file loose. I dati scritti possono essere verificati con `git fsck --strict`.

## Inizializzazione di un repository

```php
use Pitmaster\Pitmaster;

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

Questo crea:
- `.git/objects/` (archivio di oggetti vuoto)
- `.git/refs/heads/` e `.git/refs/tags/` (directory dei riferimenti vuote)
- `.git/HEAD` contenente `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` e `.git/description`
- `.git/config` con le impostazioni predefinite

Passate `'sha256'` come secondo argomento per creare un repository SHA-256: `Pitmaster::init($path, 'sha256')`.

La configurazione predefinita non imposta `core.logallrefupdates`, quindi Pitmaster non registra voci nel reflog dei repository creati finché non impostate quella chiave su `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). I repository creati da `git init` la impostano già.

## Preparazione dei file

### add

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

Ogni argomento è un percorso di file relativo alla radice dell'albero di lavoro. Le directory e i pattern glob non vengono espansi e un file mancante genera `RuntimeException`. Per ogni file, Pitmaster:
1. Legge il contenuto del file dall'albero di lavoro
2. Crea un oggetto `Blob` e ne calcola l'hash
3. Scrive il blob in `.git/objects/` (compresso con zlib)
4. Aggiorna la voce dell'indice per quel percorso con il nuovo hash e i metadati del file

### remove

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

Rimuove i percorsi tracciati dall'indice e dall'albero di lavoro. Usate `--cached` per rimuoverli solo dall'indice oppure `-r` / `--recursive` per le directory tracciate.

### mv

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

Rinomina il file nell'albero di lavoro, rimuove la vecchia voce dell'indice e ne aggiunge una nuova.

## Creazione dei commit

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

Il processo di commit:

1. **Creare l'albero dall'indice**: l'elenco piatto delle voci dell'indice viene organizzato in una gerarchia ad albero (le directory diventano oggetti tree, i file diventano riferimenti ai blob). Ogni tree viene scritto nell'archivio degli oggetti.
2. **Determinare il genitore**: il commit HEAD corrente diventa il genitore. I commit radice (il primo commit di un repository) non hanno genitori.
3. **Determinare l'autore**: viene ricavato dalle variabili d'ambiente `PITMASTER_AUTHOR_*` e `GIT_AUTHOR_*`, poi dalle costanti `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, poi da `.git/config` (`user.name`/`user.email`), infine da `Pitmaster <pitmaster@localhost>`. Il committer viene determinato nello stesso modo.
4. **Scrivere l'oggetto commit**: il contenuto del commit (`tree`, `parent`, `author`, `committer`, messaggio) viene serializzato, sottoposto a hash e scritto.
5. **Aggiornare HEAD**: il riferimento del branch a cui punta HEAD viene aggiornato al nuovo hash del commit.

Gli hook vengono eseguiti prima e dopo questi passaggi (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), a meno che l'handle non sia stato aperto con `['hooks' => false]`. `commit()` genera un errore se manca il messaggio, se l'albero è invariato rispetto a HEAD o se l'indice contiene percorsi non uniti.

### Autore personalizzato

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

## Verifica con git fsck

La copertura dei test di integrazione di Pitmaster verifica gli oggetti scritti con `git fsck --strict`; potete eseguire la stessa verifica manualmente:

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

Se Pitmaster scrive un oggetto non valido, `git fsck` segnala immediatamente l'errore.

## Gestione dei branch

### Creare un branch

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

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

Crea un file di riferimento in `.git/refs/heads/<name>` contenente l'hash del commit di destinazione.

### Eliminare un branch

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

### Cambiare branch

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

Aggiorna HEAD con un riferimento simbolico al branch, reimposta l'indice in modo che corrisponda all'albero della punta del branch e aggiorna l'albero di lavoro.

Le modifiche locali nei percorsi diversi tra i due alberi interrompono il checkout con un `RuntimeException`. Le modifiche non confermate in tutti gli altri file tracciati vengono sostituite dal contenuto del branch, nei casi in cui git le manterrebbe ([#2](https://github.com/inline0/pitmaster/issues/2)). Eseguite il commit o mettetele da parte prima di cambiare branch.

### Verificare se è stato unito

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

## Gestione dei tag

### Tag leggero

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

Un tag leggero è semplicemente un riferimento che punta direttamente a un commit.

### Tag annotato

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

Un tag annotato crea un oggetto tag (con autore del tag, messaggio e destinazione) e un riferimento che punta a esso. L'autore del tag corrisponde all'identità attuale del committer, determinata come quella dell'autore del commit.

### Autore del tag personalizzato

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

## Aggiornamento diretto dei riferimenti

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

## Scrittura diretta degli oggetti

Per i casi d'uso avanzati, potete scrivere oggetti senza passare per l'API di alto livello API.

### Scrivere 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!"
```

In un repository SHA-256 passa l'algoritmo: `Blob::fromContent('Hello, world!', 'sha256')`.

### Scrivere un albero

```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()` scrive le voci nell'ordine indicato e le stringhe di modalità così come sono. Passale nell'ordine degli alberi di git (per nome, confrontando le directory come se il loro nome terminasse con `/`) e scrivi le modalità delle directory come `40000`: git memorizza le modalità degli alberi senza lo zero iniziale e `git fsck --strict` rifiuta `040000`.

### Scrivere 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);
```

## Reset

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

## Ripristino

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

## Cherry-pick e revert

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

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

Entrambi accettano qualsiasi revisione supportata da `resolve()`. Gli hash completi funzionano; quelli abbreviati no.
