
# Zápis do repozitářů

Pitmaster zapisuje repozitáře git ve stejných binárních formátech jako kanonický binární soubor `git`. Objekty se zapisují do úložiště volných objektů (komprimované pomocí zlib), index se zapisuje ve formátu v2 (nebo v3, pokud položky vyžadují rozšířené příznaky; stávající index si zachová svou verzi) a reference se zapisují jako volné soubory. Zapsaná data lze ověřit pomocí `git fsck --strict`.

## Inicializace repozitáře

```php
use Pitmaster\Pitmaster;

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

Tím se vytvoří:
- `.git/objects/` (prázdné úložiště objektů)
- `.git/refs/heads/` a `.git/refs/tags/` (prázdné adresáře referencí)
- `.git/HEAD` obsahující `ref: refs/heads/main`
- `.git/hooks/`, `.git/info/exclude` a `.git/description`
- `.git/config` s výchozím nastavením

Předejte `'sha256'` jako druhý argument a vytvořte SHA-256 repozitář: `Pitmaster::init($path, 'sha256')`.

Výchozí konfigurace nenastavuje `core.logallrefupdates`, takže Pitmaster do vytvořeného repozitáře nezaznamenává žádné položky reflogu, dokud tento klíč nenastavíte na `true` ([#8](https://github.com/inline0/pitmaster/issues/8)). Repozitáře vytvořené pomocí `git init` už tuto hodnotu nastavenou mají.

## Příprava souborů

### add

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

Každý argument je cesta k souboru vztažená ke kořenovému adresáři pracovního stromu. Adresáře a glob vzory se nerozbalují a při chybějícím souboru se vyvolá `RuntimeException`. U každého souboru Pitmaster:
1. Načte obsah souboru z pracovního stromu
2. Vytvoří objekt `Blob` a vypočítá jeho hash
3. Zapíše blob do `.git/objects/` (komprimovaný pomocí zlib)
4. Aktualizuje položku indexu pro danou cestu novým hashem a metadaty souboru

### remove

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

Odebere sledované cesty z indexu i pracovního stromu. Pro odebrání pouze z indexu použijte `--cached`, pro sledované adresáře pak `-r` / `--recursive`.

### mv

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

Přejmenuje soubor v pracovním stromu, odebere starou položku z indexu a přidá novou.

## Vytváření potvrzení

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

Postup vytvoření potvrzení:

1. **Vytvoření stromu z indexu**: plochý seznam položek indexu se uspořádá do hierarchie stromů (adresáře se stanou objekty stromu, soubory odkazy na bloby). Každý strom se zapíše do úložiště objektů.
2. **Určení rodiče**: aktuální potvrzení HEAD se stane rodičem. Kořenová potvrzení (první potvrzení v repozitáři) žádné rodiče nemají.
3. **Určení autora**: převezme se z proměnných prostředí `PITMASTER_AUTHOR_*` a `GIT_AUTHOR_*`, poté z konstant `PITMASTER_AUTHOR_NAME`/`PITMASTER_AUTHOR_EMAIL`, dále z `.git/config` (`user.name`/`user.email`) a nakonec z `Pitmaster <pitmaster@localhost>`. Stejným způsobem se určí autor potvrzení.
4. **Zápis objektu potvrzení**: obsah potvrzení (`tree`, `parent`, `author`, `committer`, zpráva) se serializuje, zahashuje a zapíše.
5. **Aktualizace HEAD**: reference větve, na kterou HEAD ukazuje, se aktualizuje na nový hash potvrzení.

Před těmito kroky a po nich se spouštějí hooky (`pre-commit`, `prepare-commit-msg`, `commit-msg`, `post-commit`), pokud úchyt nebyl otevřen pomocí `['hooks' => false]`. `commit()` vyvolá chybu, pokud chybí zpráva, strom se od HEAD nezměnil nebo index obsahuje nesloučené cesty.

### Vlastní autor

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

## Ověření pomocí git fsck

Integrační testy Pitmaster ověřují zapsané objekty pomocí `git fsck --strict`; totéž můžete ověřit ručně:

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

Pokud Pitmaster zapíše neplatný objekt, `git fsck` chybu ihned ohlásí.

## Správa větví

### Vytvoření větve

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

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

Vytvoří soubor reference v `.git/refs/heads/<name>` obsahující hash cílového potvrzení.

### Smazání větve

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

### Přepínání větví

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

Aktualizuje HEAD na symbolickou referenci ukazující na větev, resetuje index podle stromu na konci větve a aktualizuje pracovní strom.

Místní změny v cestách, které se mezi oběma stromy liší, zabrání přepnutí větve pomocí `RuntimeException`. Nepotvrzené změny ve všech ostatních sledovaných souborech se nahradí obsahem větve, kde by je git zachoval ([#2](https://github.com/inline0/pitmaster/issues/2)). Před přepnutím je potvrďte nebo uložte do stash.

### Ověření, zda byla změna sloučena

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

## Správa tagů

### Lehký tag

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

Lehký tag je pouze reference, která ukazuje přímo na commit.

### Anotovaný tag

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

Anotovaný tag vytvoří objekt tagu (s označující osobou, zprávou a cílem) a referenci, která na něj ukazuje. Označující osoba je identita aktuálního committera určená stejně jako autor commitu.

### Vlastní označující osoba

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

## Přímá aktualizace referencí

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

## Přímý zápis objektů

V pokročilých případech použití můžete zapisovat objekty bez využití vysokoúrovňového API.

### Zápis blobu

```php
use Pitmaster\Object\Blob;

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

V repozitáři SHA-256 předejte algoritmus: `Blob::fromContent('Hello, world!', 'sha256')`.

### Zápis stromu

```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()` zapisuje položky v zadaném pořadí a řetězce režimů přesně podle zadání. Předejte je v pořadí stromu git (podle názvu, přičemž adresáře se porovnávají, jako by jejich název končil na `/`) a režimy adresářů zapište jako `40000`: git ukládá režimy stromů bez úvodní nuly a `git fsck --strict` odmítá `040000`.

### Zápis commitu

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

## Reset

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

## Obnovení

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

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

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

Obě operace přijímají libovolnou revizi, kterou podporuje `resolve()`. Úplné hashe fungují, zkrácené nikoli.
