
# Git-objekt

Allt i ett git-förråd lagras som ett objekt. Det finns fyra typer: blobs (filinnehåll), träd (kataloglistor), commits (ögonblicksbilder med metadata) och taggar (annoterade referenser). Varje objekt identifieras av innehållets SHA-1-hash.

## Objekttyper

### Blob

En blob innehåller rått filinnehåll utan metadata (inget filnamn och inga behörigheter).

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

Ett träd är en kataloglista. Varje post har ett läge, ett namn och en hash som pekar på en blob eller ett undert träd.

```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()` skriver posterna i angiven ordning och lägessträngarna exakt som de anges, så sortera dem först och skriv kataloglägen som `40000`. Ett träd som skrivs med `040000` eller i en annan ordning får en annan hash än gits, och `git fsck --strict` rapporterar läget med utfyllnad av nollor.

#### TreeEntry

`TreeEntry` är ett skrivskyddat värdeobjekt:

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

Lägesvärden:
| Läge | Betydelse |
|------|---------|
| `100644` | Vanlig fil |
| `100755` | Körbar fil |
| `40000` | Katalog (träd); visas som `040000` av `git ls-tree` |
| `120000` | Symbolisk länk |
| `160000` | Gitlink (undermodul) |

Trädposter sorteras efter namn, där katalognamn behandlas som om de slutar med `/` vid jämförelse.

### Commit

En commit knyter ett träd till dess historik och 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
```

#### Skapa commit-innehåll

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

Det råa commit-formatet:

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

Annoterade taggar är objekt med 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
```

Rått taggformat:

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

## ObjectId

`ObjectId` är ett skrivskyddat värdeobjekt som representerar en SHA-1- (eller SHA-256-)hash.

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

Hashen beräknas över okomprimerade data: `sha1("{type} {size}\0{content}")` eller SHA-256 av samma byte. `fromHex()` och `fromBinary()` väljer algoritm utifrån längden.

## ObjectType-enum

```php
use Pitmaster\Object\ObjectType;

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

## Löst objektformat

Lösa objekt lagras som enskilda filer i `.git/objects/`, organiserade efter hashens två första hexadecimala tecken.

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

Filinnehållet är zlib-komprimerat. Det okomprimerade innehållet är:

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

Exempelvis lagras en blob som innehåller "Hello" så här:

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

### Läsa lösa objekt

```php
use Pitmaster\Storage\LooseObjectStore;

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

`LooseObjectStore`:
1. Beräknar filsökvägen från hashen
2. Läser och zlib-dekomprimerar filen
3. Tolkar huvudet för att hämta typ och storlek
4. Överlåter till rätt parser (`Blob`, `Tree`, `Commit`, `Tag`)

### Skriva lösa objekt

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

Skrivprocessen:
1. Serialisera: `"{type} {size}\0{content}"`
2. Använd objektets `id`, som beräknades när objektet skapades (hashen verifieras vid läsning)
3. Komprimera: `zlib_encode(serialized)`
4. Skriv till `.git/objects/{hash[0..1]}/{rest of hash}`
5. Skriv atomiskt (skriv till en temporär fil och byt sedan namn)

## ObjectSerializer

`ObjectSerializer` hanterar kodning och avkodning av objektets trådformat.

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

`ObjectDatabase` är ett sammansatt lager som först söker bland lösa objekt och därefter i packfiler.

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

Skrivning direkt till löslagringen är avsiktlig. Låt `git gc` packa om till packfiler.

## Stöd för SHA-256

Pitmaster abstraherar hashalgoritmen genom `ObjectId`. SHA-1 är standard. SHA-256 väljs per förråd när det skapas:

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

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

Detta skriver `extensions.objectformat = sha256` till konfigurationen. Förråd som öppnas senare läser formatet från den posten, och varje åtgärd för objekt, träd, index och referenser använder då 64 tecken långa hexadecimala strängar och binära hashvärden på 32 byte. Det finns ingen global växel eller konstant för algoritmen.

Objektklasserna tar algoritmen som argument när du själv skapar objekt: `Blob::fromContent($content, 'sha256')`, `Tree::fromEntries($entries, 'sha256')`, `ObjectId::compute($type, $content, 'sha256')`. `Pitmaster::clone()` skapar SHA-1-förråd.

## Binärt format för trädposter

I innehållet i ett trädobjekt kodas varje post så här:

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

Obs! Hashen är rå binärdata, inte hexadecimal, till skillnad från hexadecimala hashvärden i commit- och taggobjekt.

Poster sorteras genom att namn jämförs som om kataloger hade ett avslutande `/`. Sorteringsordningen måste vara exakt för att trädets hash ska överensstämma med beräkningen hos git.
