
# Klooni, too, saada

Pitmaster toetab kolme põhilist giti võrgutoimingut: kloonimist (esialgne allalaadimine), toomist (täiendav allalaadimine) ja saatmist (üleslaadimine). URL-id SSH (`ssh://...`, `user@host:path`) kasutavad transporti SSH; muud URL-id kasutavad nutikat HTTP-i ning kloonimine ja toomine lähevad nutika andmevahetuse nurjumisel üle lihtsustatud HTTP-ile (vt [Protokollid](https://pitmaster.inline0.dev/et/docs/network/protocols)).

Kõik kolm käivitavad võrgutranspordid ja seetõttu tekitavad erindi `RuntimeException`, kui käepide avati valikuga `['processes' => false]` või `Pitmaster::processFreeOptions()`.

## Kloonimine

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

Uus hoidla kasutab alati SHA-1-i. Kui kloonimine nurjub ja sihtkohta polnud varem olemas, eemaldatakse osaliselt loodud kataloog.

### Kloonimise käik

1. **Initsialiseeri**: loo sihtkoha teele uus tühi hoidla
2. **Seadista kaughoidla**: salvesta URL väärtusena `remote.origin.url` ja seadista toomise viitespetsifikatsioon
3. **Tuvasta viited**: uuri käsuga `GET /info/refs?service=git-upload-pack`, mida serveris leidub
4. **Lepi kokku**: saada kõigi kaugviidete kohta `want` read (räsi alusel duplikaadid eemaldatud)
5. **Võta pakifail vastu**: laadi alla pakifail, mis sisaldab kõiki taotletud objekte
6. **Kirjuta pakifail**: salvesta pakifail asukohta `.git/objects/pack/`
7. **Indekseeri pakifail**: loo kiirete otsingute jaoks fail `.idx`
8. **Seadista viited**: loo kaugjälgitavad harud (`refs/remotes/origin/*`) ja sildid
9. **Seadista HEAD**: suuna HEAD vaikimisi harule (serveri võimaluse `symref=HEAD:...` alusel või väärtusele `refs/heads/main`, kui server ei saada võimalusi) ning määra `branch.<name>.remote` ja `branch.<name>.merge`
10. **Võta välja**: võta see haru välja, kirjutades tööpuu ja registri

Vigu viimase väljavõtmise ajal püütakse kinni ja eiratakse, seega võib kloonimisel tagastuda tühi või osaline tööpuu ([#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();
```

### Tööpuu loomine

Pärast pakifaili vastuvõtmist kutsub Pitmaster vaikimisi haru jaoks välja `$repo->checkout()`. See kirjutab HEAD-sissekande puust kõik failid töökataloogi ja koostab registri puukirjete põhjal, nii et `status()` teatab puhtast tööpuust.

## Toomine

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

### Toomise käik

1. **Loe kaughoidla URL**: failist `.git/config` (`remote.<name>.url`)
2. **Tuvasta viited**: hangi kõigi kaugviidete hetkeseis
3. **Lepi kokku taotletavate ja olemasolevate objektide üle**:
   - Taotletavad: kaugviited, mis vastavad viitespetsifikatsioonidele `remote.<name>.fetch` (vaikimisi `+refs/heads/*:refs/remotes/<name>/*`) ja mille objekte pole kohalikus hoidlas (määratud sügavuse korral kõik vastavad viited)
   - Olemasolevad: iga kohaliku viite tipp
4. **Võta pakifail vastu**: laadi alla ainult uued objektid
5. **Kirjuta pakifail**: salvesta asukohta `.git/objects/pack/` ja loo selle indeks
6. **Uuenda jälgimisviiteid**: uuenda iga sobiva kaug-haru jaoks `refs/remotes/<remote>/<branch>` ning kirjuta kõik teatatud sildiviited. Sildiobjekte eraldi ei taotleta, seega võib uus märkustega silt viidata objektile, mida alla ei laaditud ([#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()` otsib nimesid kujul `refs/heads/<name>`, `refs/tags/<name>` ja nime enda kujul, seega lühivormi `origin/main` ei leita.

Pitmaster taotleb õhukesi pakifaile, mida see veel indekseerida ei oska ([#16](https://github.com/inline0/pitmaster/issues/16)). Kui server vastab täiendavale toomisele õhukese pakifailiga, tekitab `fetch()` erindi `PackParseException`.

### Kõik on ajakohane

Kui kõik objektid, millele kaugviited osutavad, on juba kohalikus hoidlas olemas, pakifaili ei taotleta ja `fetch()` naaseb pärast viidete tuvastamist. Sel juhul ei uuendata ka jälgimisviiteid, isegi kui kaug-harud liikusid sissekannetele, mis teil juba olemas on ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Saatmine

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

### Saatmise käik

1. **Loe kaughoidla URL**: failist `.git/config`
2. **Määra saadetav sisu**: lahenda kohalik haru sissekande räsiks
3. **Tuvasta kaugviited**: hangi kaug-haru hetkeseis (vana räsi arvutamiseks)
4. **Kontrolli edasiliikumist**: kui kaug-haru pole kohaliku haru eellane, tekita enne millegi saatmist erind `RuntimeException("Push rejected: non-fast-forward update to <branch>")`
5. **Saada viite uuendus**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Saada pakifaili andmed**: pakifail objektidega, mida kaughoidla vajab
7. **Kontrolli tulemust**: server vastab iga viite kohta õnnestumise või nurjumisega

Enne uuenduse saatmist käivitub hook `pre-push`, kui hookid pole keelatud.

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

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

### Saatmise nõuded

- Praegune haru peab kohalikus hoidlas olemas olema
- Kaughoidlal peab olema seadistatud URL
- Kaughoidla peab saatmise vastu võtma (autentimine, õigused, kiire edasiliikumise poliitikad)

### Sundsaatmine

Pitmaster saadab kaugviite reklaamitud hetkeseisu vana räsi ja `push()` keeldub kohalikult uuendustest, mis pole kiired edasiliikumised. Kaug-haru ülekirjutamiseks kasutage sundimist koos kontrolliga:

```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 (toomise siseosad)

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

Taotletavate ja olemasolevate objektide läbirääkimisel koostatakse pkt-line-päring:

```
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 vastab kinnitustega ja seejärel pakifaili andmetega (võimalik, et side-band-vormingus).

## ReceivePackClient (saatmise siseosad)

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

Saatmispäringu vorming:

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

## Vigade käsitlemine

```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()` võib tekitada ka tavalise `RuntimeException` (tundmatu kaughoidla, protsessivaba käepide) või `PackParseException` (pakifaili ei saa indekseerida). Nii `ProtocolException` kui ka `PackParseException` laiendavad klassi `\RuntimeException`, seega hõlmab nende püüdmist `\RuntimeException`.

## Kaughoidla seadistus

Kaughoidlad salvestatakse asukohta `.git/config`:

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

Juurdepääs konfiguratsiooni API kaudu:

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

## Saatmise pakifaili andmed

Praegu kogub Pitmaster saatmiseks valitud sissekannetest lähtuvad objektid, mida kaughoidla praegused viited ei hõlma, ning kirjutab need algobjektidena (ilma delta-tihenduseta) pakifaili. See on funktsionaalselt korrektne, kuid loob suuremad pakifailid kui `git push`. Delta-tihenduse lisamine pakifailide kirjutamisel on kavandatud optimeerimine.
