
# Skrivning til repositories

Pitmaster skriver til git-repositories med de samme binære formater som den kanoniske `git`-binær. Objekter skrives til lageret for løse objekter (zlib-komprimeret), indekset skrives i formatet v2 (eller v3, når poster kræver udvidede flag; et eksisterende indeks beholder sin version), og refs skrives som løse filer. De skrevne data kan verificeres med `git fsck --strict`.

## Initialisering af et repository

```php
use Pitmaster\Pitmaster;

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

Dette opretter:
- `.git/objects/` (tomt objektlager)
- `.git/refs/heads/` og `.git/refs/tags/` (tomme ref-mapper)
- `.git/HEAD`, der indeholder `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` og `.git/description`
- `.git/config` med standardindstillinger

Angiv `'sha256'` som det andet argument for at oprette et SHA-256-repository: `Pitmaster::init($path, 'sha256')`.

Standardkonfigurationen angiver ikke `core.logallrefupdates`, så Pitmaster registrerer ingen reflog-poster i et repository, det har oprettet, før du sætter den nøgle til `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Repositories oprettet af `git init` har allerede denne indstilling.

## Tilføjelse af filer til staging-området

### add

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

Hvert argument er en filsti relativ til arbejdstræets rodmappe. Mapper og glob-mønstre udvides ikke, og en manglende fil udløser `RuntimeException`. For hver fil gør Pitmaster følgende:
1. Læser filindholdet fra arbejdstræet
2. Opretter et `Blob`-objekt og beregner dets hash
3. Skriver blobben til `.git/objects/` (zlib-komprimeret)
4. Opdaterer indeks-posten for stien med den nye hash og filmetadata

### remove

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

Fjerner sporede stier fra indekset og arbejdstræet. Brug `--cached` til kun at fjerne fra indekset, eller `-r` / `--recursive` til sporede mapper.

### mv

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

Omdøber filen i arbejdstræet, fjerner den gamle indeks-post og tilføjer den nye.

## Oprettelse af commits

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

Commit-processen:

1. **Byg tree fra indekset**: Den flade liste over indeks-poster organiseres i et tree-hierarki (mapper bliver til tree-objekter, filer bliver til blob-referencer). Hvert tree skrives til objektlageret.
2. **Bestem forælder**: Den aktuelle HEAD-commit bliver forælder. Rod-commits (den første commit i et repository) har ingen forældre.
3. **Bestem forfatter**: Hentes fra miljøvariablerne `PITMASTER_AUTHOR_*` og `GIT_AUTHOR_*`, derefter konstanterne `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, derefter `.git/config` (`user.name`/`user.email`) og til sidst `Pitmaster <pitmaster@localhost>`. Committeren findes på samme måde.
4. **Skriv commit-objekt**: Commit-indholdet (`tree`, `parent`, `author`, `committer`, besked) serialiseres, hashes og skrives.
5. **Opdater HEAD**: Branch-ref'en, som HEAD peger på, opdateres til den nye commit-hash.

Hooks køres før og efter disse trin (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), medmindre handle blev åbnet med `['hooks' => false]`. `commit()` udløser en fejl, hvis beskeden mangler, hvis tree'et er uændret i forhold til HEAD, eller hvis indekset indeholder umergede stier.

### Brugerdefineret forfatter

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

## Verifikation med git fsck

Integrationsdækningen for Pitmaster kontrollerer skrevne objekter mod `git fsck --strict`, og du kan selv foretage samme verifikation:

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

Hvis Pitmaster skriver et ugyldigt objekt, rapporterer `git fsck` fejlen med det samme.

## Administration af brancher

### Opret en branch

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

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

Opretter en ref-fil på `.git/refs/heads/<name>` med target-committens hash.

### Slet en branch

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

### Skift branch

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

Opdaterer HEAD til en symbolsk ref, der peger på branchen, nulstiller indekset, så det matcher branchens spids-tree, og opdaterer arbejdstræet.

Lokale ændringer i stier, der er forskellige mellem de to træer, stopper checkout med en `RuntimeException`. Ikke-committede ændringer i alle andre sporede filer erstattes af branchens indhold, hvor git ellers ville bevare dem ([#2](https://github.com/inline0/pitmaster/issues/2)). Commit eller stash dem, før du skifter.

### Kontrollér, om den er flettet

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

## Håndtering af tags

### Letvægtstag

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

Et letvægtstag er blot en ref, der peger direkte på et commit.

### Annoteret tag

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

Et annoteret tag opretter et tagobjekt (med tagger, meddelelse og mål) samt en ref, der peger på det. Taggeren er den aktuelle committer-identitet, som bestemmes på samme måde som commitforfatteren.

### Brugerdefineret tagger

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

## Direkte opdatering af refs

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

## Direkte skrivning af objekter

Til avancerede anvendelsestilfælde kan du skrive objekter uden at gå gennem den overordnede 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 et SHA-256-repository skal algoritmen angives: `Blob::fromContent('Hello, world!', 'sha256')`.

### Skriv et træ

```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 posterne i den angivne rækkefølge og bruger de angivne tilstandsstrenge. Angiv dem i gits træsortering (efter navn, hvor mapper sammenlignes, som om deres navn sluttede på `/`), og angiv mappetilstande som `40000`: git gemmer trætilstande uden det indledende nul, og `git fsck --strict` afviser `040000`.

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

## Nulstil

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

## Gendan

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

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

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

Begge accepterer enhver revision, som `resolve()` accepterer. Fuldstændige hashes virker; forkortede hashes gør ikke.
