
# Klonovanie, načítanie, odosielanie

Pitmaster podporuje tri základné sieťové operácie git: klonovanie (počiatočné stiahnutie), načítanie (priebežné stiahnutie) a odosielanie (nahrávanie). Adresy URL SSH (`ssh://...`, `user@host:path`) používajú prenos SSH; ostatné adresy URL používajú inteligentný HTTP a klonovanie aj načítanie pri zlyhaní inteligentnej výmeny prejdú na jednoduchý HTTP (pozrite si [Protokoly](https://pitmaster.inline0.dev/sk/docs/network/protocols)).

Všetky tri spúšťajú sieťové prenosy, preto pri objekte otvorenom pomocou `['processes' => false]` alebo `Pitmaster::processFreeOptions()` vyhodia výnimku `RuntimeException`.

## Klonovanie

```php
use Pitmaster\Pitmaster;

// public static function clone(string $url, string $path, ?int $depth = null, array $options = []): Repository
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/path/to/destination');
$repo = Pitmaster::clone('https://github.com/user/repo.git', '/path/to/shallow', 1);
```

Nový repozitár vždy používa SHA-1. Ak klonovanie zlyhá a cieľ predtým neexistoval, čiastočne vytvorený adresár sa odstráni.

### Priebeh klonovania

1. **Inicializácia**: vytvorte nový prázdny repozitár v cieľovej ceste
2. **Konfigurácia vzdialeného repozitára**: uložte URL ako `remote.origin.url` a nastavte špecifikáciu odkazov na načítanie
3. **Zistenie odkazov**: použite `GET /info/refs?service=git-upload-pack` na zistenie obsahu servera
4. **Dohodnutie**: odošlite riadky `want` pre všetky vzdialené odkazy (hashy sa deduplikujú)
5. **Prijatie balíka**: stiahnite súbor balíka obsahujúci všetky požadované objekty
6. **Zápis balíka**: uložte súbor balíka do `.git/objects/pack/`
7. **Indexovanie balíka**: vytvorte súbor `.idx` na rýchle vyhľadávanie
8. **Nastavenie odkazov**: vytvorte vetvy sledujúce vzdialený repozitár (`refs/remotes/origin/*`) a značky
9. **Nastavenie HEAD**: nasmerujte HEAD na predvolenú vetvu (podľa schopnosti `symref=HEAD:...` servera alebo na `refs/heads/main`, ak server žiadnu neposkytne) a nastavte `branch.<name>.remote` a `branch.<name>.merge`
10. **Checkout**: vykonajte checkout tejto vetvy; tým sa zapíše pracovný strom aj index

Chyby počas záverečného checkoutu sa zachytia a ignorujú, takže klonovanie sa môže skončiť s prázdnym alebo čiastočným pracovným stromom ([#19](https://github.com/inline0/pitmaster/issues/19)).

```php
$repo = Pitmaster::clone($url, $path);
$head = $repo->head();
echo $head->message;

$branches = $repo->branches();
$files = $repo->status();
```

### Vytvorenie pracovného stromu

Po prijatí balíka Pitmaster zavolá `$repo->checkout()` pre predvolenú vetvu. Tým sa zapíšu všetky súbory zo stromu odovzdania HEAD do pracovného adresára a z položiek stromu sa vytvorí index, takže `status()` oznámi čistý pracovný strom.

## Načítanie

```php
// public function fetch(string $remote = 'origin', ?int $depth = null): void
$repo->fetch();              // Fetch from 'origin'
$repo->fetch('upstream');    // Fetch from a named remote
$repo->fetch('origin', 1);   // Shallow fetch
```

### Priebeh načítania

1. **Čítanie vzdialeného URL**: z `.git/config` (`remote.<name>.url`)
2. **Zistenie odkazov**: zistite aktuálny stav všetkých vzdialených odkazov
3. **Dohodnutie požadovaných a dostupných objektov**:
   - Požadované: vzdialené odkazy zodpovedajúce špecifikáciám odkazov `remote.<name>.fetch` (predvolené `+refs/heads/*:refs/remotes/<name>/*`), ktorých objekty lokálne chýbajú (pri zadanej hĺbke všetky)
   - Dostupné: vrcholový objekt každej lokálnej referencie
4. **Prijatie balíka**: stiahnite iba nové objekty
5. **Zápis balíka**: uložte ho do `.git/objects/pack/` a vytvorte jeho index
6. **Aktualizácia sledovaných odkazov**: aktualizujte `refs/remotes/<remote>/<branch>` pre každú zodpovedajúcu vzdialenú vetvu a zapíšte všetky oznámené odkazy značiek. Objekty značiek sa nevyžadujú samostatne, preto môže byť nová anotovaná značka zapísaná ako odkaz na objekt, ktorý sa nestiahol ([#24](https://github.com/inline0/pitmaster/issues/24))

```php
// Fetch is incremental: only downloads what's new
$repo->fetch();

// After fetching, merge or rebase to incorporate changes (use the full ref name)
$result = $repo->merge('refs/remotes/origin/main');
```

`resolve()` vyhľadáva názvy ako `refs/heads/<name>`, `refs/tags/<name>` a samotný názov, preto sa skrátená forma `origin/main` nenájde.

Pitmaster požaduje tenké balíky, ktoré zatiaľ nedokáže indexovať ([#16](https://github.com/inline0/pitmaster/issues/16)). Ak server odpovie na priebežné načítanie tenkým balíkom, `fetch()` vyhodí výnimku `PackParseException`.

### Už aktuálne

Ak všetky objekty, na ktoré smerujú vzdialené odkazy, už existujú lokálne, balík sa nevyžiada a `fetch()` sa po zistení odkazov vráti. V takom prípade sa neaktualizujú ani sledované odkazy, a to ani vtedy, keď sa vzdialené vetvy posunuli k odoslaniam, ktoré už máte ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Odosielanie

```php
$repo->push();                        // Push current branch to 'origin'
$repo->push('origin', 'main');        // Push a specific branch
$repo->push('upstream', 'feature');   // Push to a named remote
```

### Priebeh odosielania

1. **Čítanie vzdialeného URL**: z `.git/config`
2. **Určenie zmien na odoslanie**: vyriešte lokálnu vetvu na jej hash odovzdania
3. **Zistenie vzdialených odkazov**: zistite aktuálny stav vzdialenej vetvy (na výpočet starého hashu)
4. **Kontrola posunu vpred**: ak vzdialená vetva nie je predkom lokálnej vetvy, pred odoslaním čohokoľvek vyhoďte výnimku `RuntimeException("Push rejected: non-fast-forward update to <branch>")`
5. **Odoslanie aktualizácie odkazu**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Odoslanie údajov balíka**: balík obsahujúci objekty, ktoré vzdialený repozitár potrebuje
7. **Kontrola výsledku**: server oznámi úspech alebo zlyhanie pre každý odkaz

Hook `pre-push` sa spustí pred odoslaním aktualizácie, pokiaľ nie sú hooky vypnuté.

```php
// Push the current branch
$repo->push();

// Push a specific branch to a specific remote
$repo->push('origin', 'feature/login');
```

### Požiadavky na odoslanie

- Aktuálna vetva musí existovať lokálne
- Vzdialený repozitár musí byť nakonfigurovaný s URL
- Vzdialený repozitár musí povoliť odoslanie (overenie totožnosti, oprávnenia, pravidlá proti posunu vzad)

### Vynútené odoslanie

Pitmaster odošle starý hash z aktuálneho oznámenia odkazov vzdialeného repozitára a `push()` lokálne odmieta aktualizácie s posunom vzad. Ak chcete prepísať vzdialenú vetvu, použite vynútenie s poistkou:

```php
// Lease defaults to refs/remotes/<remote>/<branch>; pass an ObjectId to set it explicitly
$repo->pushForceWithLease('origin', 'main');
$repo->pushForceWithLease('origin', 'main', $repo->resolve('refs/remotes/origin/main'));

// Update several branches in one atomic request
$repo->pushAtomic('origin', ['main', 'release']);

// Make the remote mirror local refs (also deletes remote refs that no longer exist locally)
$repo->pushMirror('origin');
```

## UploadPackClient (vnútorné fungovanie načítania)

```php
use Pitmaster\Protocol\UploadPackClient;
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient();
$uploadPack = new UploadPackClient($http);

// Fetch with explicit wants and haves
$packData = $uploadPack->fetch($url, $wants, $haves);
```

Dohodnutie požadovaných a dostupných objektov vytvorí požiadavku pkt-line:

```
want <hash1> <capabilities>\n
want <hash2>\n
deepen <depth>\n        (only when a depth is given)
0000
have <hash3>\n
have <hash4>\n
done\n
```

Server odpovie potvrdeniami a potom údajmi balíka (prípadne zabalenými do postranného pásma).

## ReceivePackClient (vnútorné fungovanie odosielania)

```php
use Pitmaster\Protocol\ReceivePackClient;
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient();
$receivePack = new ReceivePackClient($http);

$receivePack->push($url, [
    [
        'old' => $oldId,      // Current remote hash (or zero hash for new branch)
        'new' => $localId,    // Local commit hash to push
        'ref' => 'refs/heads/main',
    ],
], $packData);
```

Formát požiadavky na odoslanie:

```
<old-hash> <new-hash> refs/heads/main\0<capabilities>\n
0000
PACK<pack data>
```

## Spracovanie chýb

```php
use Pitmaster\Exceptions\ProtocolException;

try {
    $repo->fetch();
} catch (ProtocolException $e) {
    // Network unreachable, auth failed, server error, etc.
    echo $e->getMessage();
}

try {
    $repo->push();
} catch (\RuntimeException $e) {
    // Remote not configured, not on a branch, branch not found, non-fast-forward
    echo $e->getMessage();
}
```

`fetch()` môže tiež vyhodiť obyčajnú výnimku `RuntimeException` (neznámy vzdialený repozitár, objekt bez procesov) alebo `PackParseException` (balík, ktorý nemožno indexovať). `ProtocolException` a `PackParseException` sú podtriedami `\RuntimeException`, takže zachytenie `\RuntimeException` pokrýva všetky tieto výnimky.

## Konfigurácia vzdialených repozitárov

Vzdialené repozitáre sa ukladajú do `.git/config`:

```ini
[remote "origin"]
    url = https://github.com/user/repo.git
    fetch = +refs/heads/*:refs/remotes/origin/*
```

Prístup cez API konfigurácie:

```php
$config = $repo->config();
$url = $config->get('remote.origin.url');
$config->set('remote.upstream.url', 'https://github.com/other/repo.git');
$config->set('remote.upstream.fetch', '+refs/heads/*:refs/remotes/upstream/*');

// set() changes the in-memory config only; write it to persist the change
$config->writeToFile($repo->commonGitDir() . '/config');
```

## Údaje balíka na odoslanie

V súčasnosti Pitmaster zhromažďuje objekty dosiahnuteľné z odosielaných odovzdaní, ktoré nie sú dosiahnuteľné z aktuálnych odkazov vzdialeného repozitára, a zapisuje ich do balíka ako základné objekty (bez kompresie delta). Funkčne je to správne, no výsledkom sú väčšie balíky než pri použití `git push`. Kompresia delta pri zápise balíkov je plánovaným vylepšením.
