
# Git-objecten

Alles in een git-repository wordt als object opgeslagen. Er zijn vier typen: blobs (bestandsinhoud), trees (mapvermeldingen), commits (momentopnamen met metadata) en tags (geannoteerde verwijzingen). Elk object wordt geïdentificeerd aan de hand van de SHA-1-hash van de inhoud.

## Objecttypen

### Blob

Een blob bevat ruwe bestandsinhoud zonder metadata (geen bestandsnaam of rechten).

```php
use Pitmaster\Object\Blob;

// Create from content
$blob = Blob::fromContent('Hello, world!');
echo $blob->content;    // 'Hello, world!'
echo $blob->size();     // 13
echo $blob->id->hex;    // SHA-1 hash
echo $blob->type->value; // 'blob' (the property is ObjectType::Blob)
```

### Tree

Een tree is een mapvermelding. Elke vermelding heeft een modus, naam en hash die naar een blob of sub-tree verwijst.

```php
use Pitmaster\Object\Tree;
use Pitmaster\Object\TreeEntry;

// Read a tree
$tree = $repo->readObject($treeHash);

foreach ($tree->entries as $entry) {
    echo "{$entry->mode} {$entry->name} {$entry->hash->hex}\n";
}

// Look up a specific entry
$entry = $tree->entry('README.md');

// Create a tree (entries already in git's sort order)
$entries = [
    new TreeEntry('100644', 'README.md', $blobId),
    new TreeEntry('100755', 'deploy.sh', $execBlobId),
    new TreeEntry('40000', 'src', $subtreeId),
];

$tree = Tree::fromEntries($entries);
```

`Tree::fromEntries()` schrijft de vermeldingen in de opgegeven volgorde en de modustekenreeksen precies zoals opgegeven. Sorteer ze daarom eerst en schrijf mappen als `40000`. Een tree die met `040000` of in een andere volgorde wordt geschreven, krijgt een andere hash dan die van git, en `git fsck --strict` geeft de modus met voorloopnullen weer.

#### TreeEntry

`TreeEntry` is een alleen-lezen waardeobject:

```php
use Pitmaster\Object\TreeEntry;

$entry->mode;   // '100644', '100755', '40000', '120000', '160000'
$entry->name;   // Filename
$entry->hash;   // ObjectId

// Type checks
$entry->isBlob();        // regular or executable file (mode 10xxxx)
$entry->isExecutable();  // mode 100755
$entry->isTree();        // mode 40000 (also accepts 040000)
$entry->isSymlink();     // mode 120000
$entry->isGitlink();     // mode 160000 (submodule)
```

Moduswaarden:
| Modus | Betekenis |
|------|---------|
| `100644` | Gewoon bestand |
| `100755` | Uitvoerbaar bestand |
| `40000` | Map (tree); weergegeven als `040000` door `git ls-tree` |
| `120000` | Symbolische koppeling |
| `160000` | Gitlink (submodule) |

Tree-vermeldingen worden op naam gesorteerd. Voor de vergelijking worden mapnamen behandeld alsof ze eindigen op `/`.

### Commit

Een commit koppelt een tree aan de bijbehorende geschiedenis en metadata.

```php
use Pitmaster\Object\Commit;

$commit = $repo->head();

$commit->id;         // ObjectId
$commit->tree;       // ObjectId of the tree
$commit->parents;    // array of ObjectId (zero for root, one for normal, two+ for merge)
$commit->author;     // 'Name <email> timestamp timezone'
$commit->committer;  // 'Name <email> timestamp timezone'
$commit->message;    // Full commit message

$commit->isMerge();  // true if two or more parents
```

#### Commitinhoud opbouwen

```php
$content = Commit::buildContent(
    tree: $treeId,
    parents: [$parentId],
    author: 'Jane <jane@example.com> 1700000000 +0000',
    committer: 'Jane <jane@example.com> 1700000000 +0000',
    message: "Add feature\n",
);
```

De onbewerkte commitindeling:

```
tree <hex hash>\n
parent <hex hash>\n       (zero or more)
author <name> <<email>> <timestamp> <tz>\n
committer <name> <<email>> <timestamp> <tz>\n
\n
<message>
```

### Tag

Geannoteerde tags zijn objecten met metadata.

```php
use Pitmaster\Object\Tag;

$tag = $repo->readObject($tagHash);

$tag->object;        // ObjectId of the target
$tag->objectType;    // ObjectType enum (ObjectType::Commit, ...)
$tag->name;          // Tag name
$tag->tagger;        // 'Name <email> timestamp timezone'
$tag->message;       // Tag message
```

Onbewerkte tagindeling:

```
object <hex hash>\n
type commit\n
tag <name>\n
tagger <name> <<email>> <timestamp> <tz>\n
\n
<message>
```

## ObjectId

`ObjectId` is een alleen-lezen waardeobject dat een hash van SHA-1 (of SHA-256) vertegenwoordigt.

```php
use Pitmaster\Object\ObjectId;
use Pitmaster\Object\ObjectType;

// From hex string
$id = ObjectId::fromHex('a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2');

// From binary (20 bytes for SHA-1, 32 for SHA-256)
$id = ObjectId::fromBinary($rawBytes);

// Compute from content
$id = ObjectId::compute(ObjectType::Blob, 'Hello, world!');
$id = ObjectId::compute(ObjectType::Blob, 'Hello, world!', 'sha256');

// Zero hash
$id = ObjectId::zero();
$id = ObjectId::zero('sha256');

// Properties
$id->hex;      // 40-character hex string (64 for SHA-256)
$id->binary;   // 20-byte raw binary (32 for SHA-256)
$id->algo;     // 'sha1' or 'sha256'
$id->isSha256();
$id->isZero();

// Comparison
$id->equals($otherId);
```

De hash wordt berekend over de niet-gecomprimeerde gegevens: `sha1("{type} {size}\0{content}")` of SHA-256 van dezelfde bytes. `fromHex()` en `fromBinary()` kiezen het algoritme op basis van de lengte.

## ObjectType-enum

```php
use Pitmaster\Object\ObjectType;

ObjectType::Blob;    // 'blob'
ObjectType::Tree;    // 'tree'
ObjectType::Commit;  // 'commit'
ObjectType::Tag;     // 'tag'
```

## Indeling van losse objecten

Losse objecten worden opgeslagen als afzonderlijke bestanden in `.git/objects/`, geordend op de eerste twee hexadecimale tekens van hun hash.

```
.git/objects/a1/b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2
```

De bestandsinhoud is gecomprimeerd met zlib. De niet-gecomprimeerde inhoud is:

```
<type> <decimal size>\0<raw content>
```

Een blob met "Hello" wordt bijvoorbeeld als volgt opgeslagen:

```
zlib_encode("blob 5\0Hello")
```

### Losse objecten lezen

```php
use Pitmaster\Storage\LooseObjectStore;

$store = new LooseObjectStore('/path/to/.git/objects');
$object = $store->read($objectId);
```

De `LooseObjectStore`:
1. Berekent het bestandspad op basis van de hash
2. Leest het bestand en decomprimeert het met zlib
3. Parseert de koptekst om het type en de grootte uit te lezen
4. Delegeert aan de bijbehorende parser (`Blob`, `Tree`, `Commit`, `Tag`)

### Losse objecten schrijven

```php
$store->write($object);
```

Het schrijfproces:
1. Serialiseren: `"{type} {size}\0{content}"`
2. De `id` van het object gebruiken, berekend toen het object werd opgebouwd (bij het lezen wordt de hash gecontroleerd)
3. Comprimeren: `zlib_encode(serialized)`
4. Schrijven naar `.git/objects/{hash[0..1]}/{rest of hash}`
5. Atomair schrijven (naar een tijdelijk bestand schrijven en het daarna hernoemen)

## ObjectSerializer

De `ObjectSerializer` verzorgt de codering en decodering van het object-wire-formaat.

```php
use Pitmaster\Storage\ObjectSerializer;

// Encode
$raw = ObjectSerializer::encodeRaw($object);
// "blob 5\0Hello"
$compressed = ObjectSerializer::encode($object);
// zlib-compressed form, as stored in a loose object file

// Decode
$object = ObjectSerializer::decodeRaw($raw);
$object = ObjectSerializer::decode($compressed, $expectedHex); // throws CorruptObjectException on a hash mismatch
```

## ObjectDatabase

De `ObjectDatabase` is een samengestelde opslag die eerst naar losse objecten zoekt en daarna naar packbestanden.

```php
use Pitmaster\Storage\ObjectDatabase;

$db = new ObjectDatabase('/path/to/.git/objects');

$object = $db->read($objectId);    // Search loose, then packs
$db->write($object);               // Always writes to loose
$exists = $db->exists($objectId);  // Check loose, then packs
$all = $db->listAll();             // All hashes from loose + packs
```

Wegschrijven naar losse opslag is bewust zo ontworpen. Laat `git gc` de objecten herpakken in packbestanden.

## Ondersteuning voor SHA-256

Pitmaster abstraheert het hash-algoritme via `ObjectId`. SHA-1 is de standaard. SHA-256 wordt gekozen per repository wanneer deze wordt aangemaakt:

```php
$repo = Pitmaster::init('/path/to/project', 'sha256');
```

```bash
./vendor/bin/pitmaster init --object-format=sha256
```

Hiermee wordt `extensions.objectformat = sha256` naar de configuratie geschreven. Later geopende repositories lezen de indeling uit die instelling; daarna gebruiken alle bewerkingen met objecten, trees, indexen en refs hextekenreeksen van 64 tekens en binaire hashes van 32 bytes. Er is geen algemene schakelaar of constante voor het algoritme.

De objectklassen krijgen het algoritme als argument wanneer je zelf objecten aanmaakt: `Blob::fromContent($content, 'sha256')`, `Tree::fromEntries($entries, 'sha256')`, `ObjectId::compute($type, $content, 'sha256')`. `Pitmaster::clone()` maakt SHA-1-repositories aan.

## Binair formaat van tree-vermeldingen

Binnen de inhoud van een tree-object wordt elke vermelding als volgt gecodeerd:

```
<octal mode as ASCII> <space> <filename> \0 <binary hash: 20 bytes SHA-1, 32 bytes SHA-256>
```

Opmerking: de hash bestaat uit ruwe binaire gegevens, geen hexadecimale tekens zoals de hashes in commit- en tag-objecten.

Vermeldingen worden gesorteerd door namen te vergelijken alsof mappen eindigen op `/`. Deze sorteervolgorde moet exact kloppen om de hash van de tree overeen te laten komen met de berekening van git.
