
# Skriva till arkiv

Pitmaster skriver git-arkiv med samma binära format som den kanoniska `git`-binären. Objekt skrivs till lagret med lösa objekt (zlib-komprimerade), indexet skrivs i formatet v2 (eller v3 när poster behöver utökade flaggor; ett befintligt index behåller sin version) och refs skrivs som lösa filer. Data som skrivits kan verifieras med `git fsck --strict`.

## Initiera ett arkiv

```php
use Pitmaster\Pitmaster;

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

Detta skapar:
- `.git/objects/` (tomt objektlager)
- `.git/refs/heads/` och `.git/refs/tags/` (tomma ref-kataloger)
- `.git/HEAD` som innehåller `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` och `.git/description`
- `.git/config` med standardinställningar

Skicka med `'sha256'` som det andra argumentet för att skapa ett SHA-256-arkiv: `Pitmaster::init($path, 'sha256')`.

Standardkonfigurationen anger inte `core.logallrefupdates`, så Pitmaster registrerar inga reflog-poster i arkiv som det har skapat förrän du ställer in nyckeln till `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Arkiv som skapats av `git init` har redan denna inställning.

## Mellanlagra filer

### add

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

Varje argument är en filsökväg relativ till arbetsträdets rot. Kataloger och globmönster expanderas inte, och en saknad fil utlöser `RuntimeException`. För varje fil gör Pitmaster följande:
1. Läser filinnehållet från arbetsträdet
2. Skapar ett `Blob`-objekt och beräknar dess hash
3. Skriver blobben till `.git/objects/` (zlib-komprimerad)
4. Uppdaterar indexposten för sökvägen med den nya hashen och filmetadata

### remove

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

Tar bort spårade sökvägar från indexet och arbetsträdet. Använd `--cached` för att bara ta bort från indexet, eller `-r` / `--recursive` för spårade kataloger.

### mv

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

Byter namn på filen i arbetsträdet, tar bort den gamla indexposten och lägger till den nya.

## Skapa incheckningar

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

Incheckningsprocessen:

1. **Bygg ett träd från indexet**: den platta listan med indexposter organiseras i en trädstruktur (kataloger blir trädobjekt och filer blir blob-referenser). Varje träd skrivs till objektlagret.
2. **Fastställ förälder**: den aktuella HEAD-incheckningen blir förälder. Rotincheckningar (den första incheckningen i ett arkiv) har inga föräldrar.
3. **Fastställ upphovsperson**: hämtas från miljövariablerna `PITMASTER_AUTHOR_*` och `GIT_AUTHOR_*`, sedan konstanterna `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, därefter `.git/config` (`user.name`/`user.email`) och sist `Pitmaster <pitmaster@localhost>`. Incheckaren fastställs på samma sätt.
4. **Skriv incheckningsobjektet**: incheckningens innehåll (`tree`, `parent`, `author`, `committer`, meddelande) serialiseras, hashas och skrivs.
5. **Uppdatera HEAD**: grenens ref som HEAD pekar på uppdateras till den nya incheckningens hash.

Krokar körs före och efter dessa steg (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`) om inte handtaget öppnades med `['hooks' => false]`. `commit()` utlöser ett fel om meddelandet saknas, om trädet är oförändrat jämfört med HEAD eller om indexet innehåller sammanfogningssökvägar.

### Anpassad upphovsperson

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

## Verifiera med git fsck

Pitmasters integrationstester kontrollerar skrivna objekt mot `git fsck --strict`, och du kan verifiera dem manuellt på samma sätt:

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

Om Pitmaster skriver ett ogiltigt objekt rapporterar `git fsck` felet omedelbart.

## Hantera grenar

### Skapa en gren

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

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

Skapar en ref-fil på `.git/refs/heads/<name>` som innehåller mål-incheckningens hash.

### Ta bort en gren

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

### Byt gren

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

Uppdaterar HEAD till en symbolisk ref som pekar på grenen, återställer indexet så att det matchar grenens trädtopp och uppdaterar arbetsträdet.

Lokala ändringar i sökvägar som skiljer sig mellan de två träden avbryter utcheckningen med ett `RuntimeException`. Oincheckade ändringar i alla andra spårade filer ersätts med grenens innehåll, medan git skulle behålla dem ([#2](https://github.com/inline0/pitmaster/issues/2)). Checka in eller stash:a dem innan du byter gren.

### Kontrollera om sammanfogad

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

## Hantera taggar

### Lättviktig tagg

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

En lättviktig tagg är bara en ref som pekar direkt på en incheckning.

### Annoterad tagg

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

En annoterad tagg skapar ett taggobjekt (med taggare, meddelande och mål) och en ref som pekar på det. Taggaren är den aktuella incheckarens identitet, fastställd på samma sätt som incheckningens upphovsperson.

### Anpassad taggare

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

## Uppdatera refs direkt

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

## Skriva objekt direkt

För avancerade användningsfall kan du skriva objekt utan att gå via det överordnade API.

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

I ett SHA-256-arkiv anger du algoritmen: `Blob::fromContent('Hello, world!', 'sha256')`.

### Skriv ett träd

```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()` skriver posterna i den angivna ordningen och lägessträngarna som de anges. Skicka dem i trädordningen för git (efter namn, där kataloger jämförs som om namnet slutade med `/`) och ange kataloglägen som `40000`: git lagrar trädens lägen utan inledande nolla, och `git fsck --strict` avvisar `040000`.

### Skriv en incheckning

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

## Återställning

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

## Återskapa

```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 och återställning

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

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

Båda accepterar valfri revision som `resolve()` godtar. Fullständiga hashar fungerar; förkortade hashar gör det inte.
