
# Kloonaa, nouda, lähetä

Pitmaster tukee kolmea keskeistä git-verkkotoimintoa: kloonausta (ensimmäinen lataus), noutoa (lisälataus) ja lähetystä (lataus palvelimelle). SSH URL-osoitteissa (`ssh://...`, `user@host:path`) käytetään SSH-siirtoa; muissa URL-osoitteissa käytetään älykästä HTTP-protokollaa, ja kloonaus ja nouto siirtyvät tarvittaessa yksinkertaiseen HTTP-protokollaan, jos älykäs tiedonsiirto epäonnistuu (katso [Protokollat](https://pitmaster.inline0.dev/fi/docs/network/protocols)).

Kaikki kolme käynnistävät verkkosiirron, joten ne aiheuttavat virheen `RuntimeException`, jos kahva on avattu valitsimella `['processes' => false]` tai `Pitmaster::processFreeOptions()`.

## Kloonaus

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

Uusi tietovarasto käyttää aina SHA-1. Jos kloonaus epäonnistuu eikä kohdetta ollut ennestään, osittain luotu hakemisto poistetaan.

### Kloonauksen kulku

1. **Alusta**: luo tyhjä tietovarasto kohdepolkuun
2. **Määritä etävarasto**: tallenna URL muodossa `remote.origin.url` ja määritä noudon refspec
3. **Selvitä viitteet**: `GET /info/refs?service=git-upload-pack` selvittääksesi, mitä palvelimella on
4. **Neuvottele**: lähetä `want`-rivit kaikista etäviitteistä (hashin mukaan deduplikoituna)
5. **Vastaanota paketti**: lataa pakettitiedosto, joka sisältää kaikki pyydetyt objektit
6. **Kirjoita paketti**: tallenna pakettitiedosto kohteeseen `.git/objects/pack/`
7. **Indeksoi paketti**: luo `.idx`-tiedosto nopeita hakuja varten
8. **Määritä viitteet**: luo etähaaroja seuraavat haarat (`refs/remotes/origin/*`) ja tunnisteet
9. **Aseta HEAD**: kohdista HEAD oletushaaraan (palvelimen `symref=HEAD:...`-ominaisuuden mukaan tai arvoon `refs/heads/main`, jos palvelin ei ilmoita sitä) ja aseta `branch.<name>.remote` ja `branch.<name>.merge`
10. **Ota käyttöön**: ota haara käyttöön, jolloin työhakemisto ja indeksi kirjoitetaan

Viimeisen käyttöönoton virheet ohitetaan, joten kloonaus voi palauttaa tyhjän tai osittaisen työhakemiston ([#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();
```

### Työhakemiston luonti

Pakettitiedoston vastaanottamisen jälkeen Pitmaster kutsuu toimintoa `$repo->checkout()` oletushaaralle. Se kirjoittaa HEAD-commitin puun kaikki tiedostot työhakemistoon ja muodostaa indeksin puun merkinnöistä, joten `status()` ilmoittaa työhakemiston olevan puhdas.

## Nouto

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

### Noudon kulku

1. **Lue etävaraston URL**: kohteesta `.git/config` (`remote.<name>.url`)
2. **Selvitä viitteet**: hae kaikkien etäviitteiden nykytila
3. **Neuvottele halutuista ja olemassa olevista objekteista**:
   - Halutut: etäviitteet, jotka vastaavat `remote.<name>.fetch`-refspec-määrityksiä (oletuksena `+refs/heads/*:refs/remotes/<name>/*`) ja joiden objekteja ei ole paikallisesti (kaikki, jos syvyys on määritetty)
   - Olemassa olevat: jokaisen paikallisen viitteen kärki
4. **Vastaanota paketti**: lataa vain uudet objektit
5. **Kirjoita paketti**: tallenna kohteeseen `.git/objects/pack/` ja luo sen indeksi
6. **Päivitä seurantaviitteet**: päivitä `refs/remotes/<remote>/<branch>` jokaista vastaavaa etähaaraa varten ja kirjoita jokainen ilmoitettu tunnisteviite. Tunnisteobjekteja ei pyydetä erikseen, joten uusi selitetty tunniste voidaan kirjoittaa viitteeksi objektiin, jota ei ladattu ([#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()` etsii nimiä muodoissa `refs/heads/<name>`, `refs/tags/<name>` ja nimi sellaisenaan, joten lyhyttä muotoa `origin/main` ei löydy.

Pitmaster pyytää ohuita paketteja, joita se ei vielä osaa indeksoida ([#16](https://github.com/inline0/pitmaster/issues/16)). Jos palvelin vastaa lisänoutoon ohuella paketilla, `fetch()` aiheuttaa virheen `PackParseException`.

### Ajantasalla

Jos kaikki etäviitteiden osoittamat objektit ovat jo paikallisesti, pakettia ei pyydetä ja `fetch()` palautuu viitteiden selvittämisen jälkeen. Tällöin seurantaviitteitä ei myöskään päivitetä, vaikka etähaarat olisivat siirtyneet committeihin, jotka sinulla jo on ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Lähetys

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

### Lähetyksen kulku

1. **Lue etävaraston URL**: kohteesta `.git/config`
2. **Määritä lähetettävät tiedot**: selvitä paikallisen haaran commit-hash
3. **Selvitä etäviitteet**: hae etähaaran nykytila (vanhan hashin laskemista varten)
4. **Tarkista pikasiirto**: jos etähaara ei ole paikallisen haaran edeltäjä, aiheuta virhe `RuntimeException("Push rejected: non-fast-forward update to <branch>")` ennen lähetystä
5. **Lähetä viitepäivitys**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Lähetä pakettitiedot**: paketti, joka sisältää etävaraston tarvitsemat objektit
7. **Tarkista tulos**: palvelin ilmoittaa onnistumisesta tai epäonnistumisesta viitekohtaisesti

`pre-push`-koukku suoritetaan ennen päivityksen lähettämistä, ellei koukkuja ole poistettu käytöstä.

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

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

### Lähetyksen vaatimukset

- Nykyisen haaran on oltava olemassa paikallisesti
- Etävarasto on määritettävä valitsimella URL
- Etävaraston on hyväksyttävä lähetys (todennus, käyttöoikeudet, pikasiirron estävät käytännöt)

### Pakotettu lähetys

Pitmaster lähettää etävaraston aktiivisten viitteiden ilmoituksesta saadun vanhan hashin, ja `push()` estää paikallisesti muut kuin pikasiirrot. Voit korvata etähaaran käyttämällä force-with-lease-toimintoa:

```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 (noudon sisäinen toteutus)

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

Haluttujen ja olemassa olevien objektien neuvottelussa muodostetaan pkt-line-pyyntö:

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

Palvelin vastaa kuittauksilla ja lähettää sitten pakettitiedot (mahdollisesti side-band-käärittyinä).

## ReceivePackClient (lähetyksen sisäinen toteutus)

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

Lähetyspyynnön muoto:

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

## Virheenkäsittely

```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()` voi aiheuttaa myös tavallisen `RuntimeException`-virheen (tuntematon etävarasto, kahva ei käynnistä prosesseja) tai `PackParseException`-virheen (pakettia ei voi indeksoida). Sekä `ProtocolException` että `PackParseException` laajentavat `\RuntimeException`-luokkaa, joten `\RuntimeException` kattaa ne kaikki.

## Etävaraston määritys

Etävarastot tallennetaan kohteeseen `.git/config`:

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

Määritykseen pääsee API-asetuksen kautta:

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

## Lähetyksen pakettitiedot

Tällä hetkellä Pitmaster kerää lähetettävien commitien kautta saavutettavat objektit, jotka eivät ole saavutettavissa etävaraston nykyisistä viitteistä, ja kirjoittaa ne pakettiin perusobjekteina (ilman delta-pakkausta). Tämä toimii oikein, mutta tuottaa suurempia paketteja kuin `git push`. Delta-pakkaus paketteja kirjoitettaessa on suunnitteilla oleva optimointi.
