
# Klonuoti, gauti, siųsti

Pitmaster palaiko tris pagrindines git tinklo operacijas: klonavimą (pradinį atsisiuntimą), gavimą (papildomą atsisiuntimą) ir siuntimą (įkėlimą). SSH URL (`ssh://...`, `user@host:path`) naudoja SSH transportą; kiti URL naudoja išmanųjį HTTP, o klonuojant ir gaunant, nepavykus išmaniesiems mainams, pereinama prie paprastojo HTTP (žr. [Protokolai](https://pitmaster.inline0.dev/lt/docs/network/protocols)).

Visos trys operacijos paleidžia tinklo transportus, todėl atidarius saugyklos nuorodą su `['processes' => false]` arba `Pitmaster::processFreeOptions()`, jos sukelia `RuntimeException`.

## Klonuoti

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

Naujoje saugykloje visada naudojamas SHA-1. Jei klonuoti nepavyksta, o paskirties vieta anksčiau neegzistavo, iš dalies sukurtas katalogas pašalinamas.

### Klonavimo eiga

1. **Inicijuoti**: paskirties kelyje sukurti naują tuščią saugyklą
2. **Konfigūruoti nuotolinę saugyklą**: išsaugoti URL kaip `remote.origin.url` ir nustatyti gavimo refspec
3. **Aptikti nuorodas**: atlikti `GET /info/refs?service=git-upload-pack`, kad sužinotumėte, ką turi serveris
4. **Derėtis**: siųsti `want` eilutes, skirtas visoms nuotolinėms nuorodoms (pašalinant pasikartojančias maišas)
5. **Gauti paketą**: atsisiųsti paketo failą su visais prašomais objektais
6. **Įrašyti paketą**: išsaugoti paketo failą į `.git/objects/pack/`
7. **Indeksuoti paketą**: sugeneruoti `.idx` failą greitoms paieškoms
8. **Nustatyti nuorodas**: sukurti nuotolinio sekimo šakas (`refs/remotes/origin/*`) ir žymas
9. **Nustatyti HEAD**: nukreipti HEAD į numatytąją šaką (iš serverio galimybės `symref=HEAD:...` arba į `refs/heads/main`, jei serveris jos nenurodo) ir nustatyti `branch.<name>.remote` bei `branch.<name>.merge`
10. **Išimti**: išimti šią šaką; taip įrašomas darbinis medis ir indeksas

Galutinio išėmimo klaidos pagaunamos ir ignoruojamos, todėl klonavimas gali baigtis tuščiu arba daliniu darbiniu medžiu ([#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();
```

### Darbinio medžio sukūrimas

Gavusi paketą, Pitmaster iškviečia `$repo->checkout()` numatytajai šakai. Taip visi HEAD įrašo medžio failai įrašomi į darbinį katalogą, o indeksas sudaromas iš medžio įrašų, todėl `status()` nurodo, kad darbinis medis švarus.

## Gauti

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

### Gavimo eiga

1. **Skaityti nuotolinės saugyklos URL**: iš `.git/config` (`remote.<name>.url`)
2. **Aptikti nuorodas**: gauti dabartinę visų nuotolinių nuorodų būseną
3. **Derėtis dėl norimų ir turimų objektų**:
   - Norimi: nuotolinės nuorodos, atitinkančios `remote.<name>.fetch` refspec'us (pagal numatytuosius nustatymus `+refs/heads/*:refs/remotes/<name>/*`), kurių objektų nėra vietoje (jei nurodytas gylis, įtraukiamos visos)
   - Turimi: visų vietinių nuorodų viršūnės
4. **Gauti paketą**: atsisiųsti tik naujus objektus
5. **Įrašyti paketą**: išsaugoti į `.git/objects/pack/` ir sudaryti jo indeksą
6. **Atnaujinti sekimo nuorodas**: atnaujinti kiekvieną atitinkančios nuotolinės šakos `refs/remotes/<remote>/<branch>` nuorodą ir įrašyti kiekvieną paskelbtą žymos nuorodą. Žymų objektų atskirai neprašoma, todėl nauja anotuota žyma gali būti įrašyta kaip nuoroda į neatsisiųstą objektą ([#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()` pavadinimų ieško kaip `refs/heads/<name>`, `refs/tags/<name>` ir kaip paties pavadinimo, todėl trumpasis variantas `origin/main` nerandamas.

Pitmaster prašo plonų paketų, kurių kol kas negali indeksuoti ([#16](https://github.com/inline0/pitmaster/issues/16)). Jei serveris atsako į papildomą gavimo užklausą plonu paketu, `fetch()` sukelia `PackParseException`.

### Jau atnaujinta

Jei visi objektai, į kuriuos nurodo nuotolinės nuorodos, jau yra vietoje, paketo neprašoma, o `fetch()` baigia darbą aptikusi nuorodas. Tokiu atveju sekimo nuorodos taip pat neatnaujinamos, net jei nuotolinės šakos pasikeitė į jau turimus įrašus ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Siųsti

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

### Siuntimo eiga

1. **Skaityti nuotolinės saugyklos URL**: iš `.git/config`
2. **Nustatyti, ką siųsti**: išspręsti vietinę šaką iki jos įrašo maišos
3. **Aptikti nuotolines nuorodas**: gauti dabartinę nuotolinės šakos būseną (senajai maišai apskaičiuoti)
4. **Patikrinti, ar galima perkelti pirmyn**: jei nuotolinė šaka nėra vietinės šakos protėvis, prieš siunčiant ką nors sukeliama `RuntimeException("Push rejected: non-fast-forward update to <branch>")`
5. **Siųsti nuorodos atnaujinimą**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Siųsti paketo duomenis**: paketas su objektais, kurių reikia nuotolinei saugyklai
7. **Patikrinti rezultatą**: serveris pateikia kiekvienos nuorodos sėkmės arba klaidos būseną

`pre-push` hook vykdomas prieš siunčiant atnaujinimą, nebent hook išjungti.

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

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

### Siuntimo reikalavimai

- Dabartinė šaka turi egzistuoti vietoje
- Nuotolinei saugyklai turi būti sukonfigūruotas URL
- Nuotolinė saugykla turi priimti siuntimą (autentifikavimas, leidimai, politika dėl neperkeliamų pirmyn pakeitimų)

### Priverstinis siuntimas

Pitmaster siunčia senąją maišą iš tuo metu pateikiamos nuotolinės nuorodos reklamos, o `push()` vietoje atmeta neperkeliamus pirmyn atnaujinimus. Norėdami perrašyti nuotolinę šaką, naudokite force-with-lease:

```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 (gavimo vidinė veikimo dalis)

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

Norimų ir turimų objektų derinimo metu sudaroma pkt-line užklausa:

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

Serveris atsako patvirtinimais, o tada pateikia paketo duomenis (galbūt įvyniotus į side-band).

## ReceivePackClient (siuntimo vidinė veikimo dalis)

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

Siuntimo užklausos formatas:

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

## Klaidų tvarkymas

```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()` taip pat gali sukelti paprastą `RuntimeException` (nežinoma nuotolinė saugykla, nuoroda be procesų) arba `PackParseException` (paketas, kurio negalima indeksuoti). Tiek `ProtocolException`, tiek `PackParseException` išplečia `\RuntimeException`, todėl pagavę `\RuntimeException` apimsite visas šias klaidas.

## Nuotolinės saugyklos konfigūracija

Nuotolinės saugyklos įrašomos į `.git/config`:

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

Pasiekiama per konfigūracijos API:

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

## Paketo duomenys siuntimui

Šiuo metu Pitmaster surenka objektus, pasiekiamus iš siunčiamų įrašų, kurių nepasiekia dabartinės nuotolinės nuorodos, ir įrašo juos į paketą kaip bazinius objektus (be deltų glaudinimo). Tai veikia teisingai, tačiau paketai būna didesni nei naudojant `git push`. Deltų glaudinimas rašant paketus numatytas kaip būsimas optimizavimas.
