
# Schrijven naar repositories

Pitmaster schrijft git-repositories met dezelfde binaire indelingen als de canonieke git-binary `git`. Objecten worden naar de opslag voor losse objecten geschreven (zlib-gecomprimeerd), de index wordt geschreven in v2-indeling (of v3 wanneer items uitgebreide vlaggen vereisen; een bestaande index behoudt zijn versie) en refs worden als losse bestanden geschreven. De geschreven gegevens kunnen worden geverifieerd met `git fsck --strict`.

## Een repository initialiseren

```php
use Pitmaster\Pitmaster;

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

Dit maakt het volgende aan:
- `.git/objects/` (lege objectopslag)
- `.git/refs/heads/` en `.git/refs/tags/` (lege refmappen)
- `.git/HEAD` met `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` en `.git/description`
- `.git/config` met standaardinstellingen

Geef `'sha256'` als tweede argument mee om een SHA-256-repository aan te maken: `Pitmaster::init($path, 'sha256')`.

De standaardconfiguratie stelt `core.logallrefupdates` niet in, dus Pitmaster legt geen reflog-items vast in een repository die hiermee is aangemaakt totdat u die sleutel op `true` instelt ([#8](https://github.com/inline0/pitmaster/issues/8)). Repositories die door `git init` zijn aangemaakt, hebben deze instelling al.

## Bestanden stagen

### add

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

Elk argument is een bestandspad relatief aan de root van de werkboom. Mappen en globpatronen worden niet uitgebreid en bij een ontbrekend bestand wordt `RuntimeException` gegenereerd. Voor elk bestand doet Pitmaster het volgende:
1. Leest de bestandsinhoud uit de werkboom
2. Maakt een `Blob`-object aan en berekent de hash ervan
3. Schrijft de blob naar `.git/objects/` (zlib-gecomprimeerd)
4. Werkt de indexvermelding voor dat pad bij met de nieuwe hash en bestandsmetagegevens

### remove

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

Verwijdert bijgehouden paden uit de index en werkboom. Gebruik `--cached` om alleen uit de index te verwijderen, of `-r` / `--recursive` voor bijgehouden mappen.

### mv

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

Hernoemt het bestand in de werkboom, verwijdert de oude indexvermelding en voegt de nieuwe toe.

## Commits aanmaken

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

Het commitproces:

1. **Tree opbouwen vanuit de index**: de vlakke lijst met indexitems wordt omgezet in een tree-hiërarchie (mappen worden tree-objecten, bestanden blobverwijzingen). Elke tree wordt naar de objectopslag geschreven.
2. **Ouder bepalen**: de huidige HEAD-commit wordt de ouder. Rootcommits (de eerste commit in een repo) hebben geen ouders.
3. **Auteur bepalen**: afkomstig uit de omgevingsvariabelen `PITMASTER_AUTHOR_*` en `GIT_AUTHOR_*`, vervolgens de constanten `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, daarna `.git/config` (`user.name`/`user.email`) en ten slotte `Pitmaster <pitmaster@localhost>`. De committer wordt op dezelfde manier bepaald.
4. **Commitobject schrijven**: de commitinhoud (`tree`, `parent`, `author`, `committer`, bericht) wordt geserialiseerd, gehasht en geschreven.
5. **HEAD bijwerken**: de branchref waar HEAD naar verwijst, wordt bijgewerkt met de nieuwe commithash.

Hooks worden rond deze stappen uitgevoerd (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), tenzij de handle is geopend met `['hooks' => false]`. `commit()` genereert een fout als het bericht ontbreekt, als de tree ongewijzigd is ten opzichte van HEAD of als de index niet-samengevoegde paden bevat.

### Aangepaste auteur

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

## Verifiëren met git fsck

De integratiedekking van Pitmaster controleert geschreven objecten aan de hand van `git fsck --strict`; u kunt dit ook zelf handmatig verifiëren:

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

Als Pitmaster een ongeldig object schrijft, meldt `git fsck` de fout direct.

## Branches beheren

### Een branch aanmaken

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

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

Maakt een refbestand aan op `.git/refs/heads/<name>` met de hash van de doelcommit.

### Een branch verwijderen

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

### Van branch wisselen

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

Werkt HEAD bij naar een symbolische ref die naar de branch verwijst, zet de index terug zodat deze overeenkomt met de tree van de branchkop en werkt de werkboom bij.

Lokale wijzigingen in paden die verschillen tussen de twee trees stoppen het uitchecken met een `RuntimeException`. Niet-vastgelegde wijzigingen in alle andere bijgehouden bestanden worden vervangen door de inhoud van de branch, waar git ze zou behouden ([#2](https://github.com/inline0/pitmaster/issues/2)). Commit of stash ze voordat u van branch wisselt.

### Controleren of samengevoegd

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

## Tags beheren

### Lichtgewicht tag

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

Een lichtgewicht tag is slechts een ref die rechtstreeks naar een commit verwijst.

### Geannoteerde tag

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

Een geannoteerde tag maakt een tagobject aan (met tagger, bericht en doel) en een ref die ernaar verwijst. De tagger is de huidige committeridentiteit, die op dezelfde manier wordt bepaald als de commit-auteur.

### Aangepaste tagger

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

## Refs rechtstreeks bijwerken

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

## Objecten rechtstreeks schrijven

Voor geavanceerde toepassingen kunt u objecten schrijven zonder de high-level API te gebruiken.

### Een blob schrijven

```php
use Pitmaster\Object\Blob;

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

Geef het algoritme in een SHA-256-repository door: `Blob::fromContent('Hello, world!', 'sha256')`.

### Een tree schrijven

```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()` schrijft de entries in de opgegeven volgorde en met de opgegeven mode-strings. Geef ze op in de tree-volgorde van git (op naam, waarbij directories worden vergeleken alsof hun naam eindigt op `/`) en schrijf directory-modes als `40000`: git slaat tree-modes op zonder de voorloopnul, en `git fsck --strict` weigert `040000`.

### Een commit schrijven

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

## Resetten

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

## Herstellen

```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-picken en terugdraaien

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

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

Beide accepteren elke revision die `resolve()` accepteert. Volledige hashes werken; verkorte hashes niet.
