
# Klónozás, lekérés, feltöltés

Az Pitmaster a git három alapvető hálózati műveletét támogatja: a klónozást (első letöltés), a lekérést (részleges letöltés) és a feltöltést (küldés). A SSH URL-ek (`ssh://...`, `user@host:path`) a SSH átvitelt használják; más URL-ek intelligens HTTP protokollt használnak, a klónozás és a lekérés pedig buta HTTP protokollra vált, ha az intelligens adatcsere sikertelen (lásd: [Protokollok](https://pitmaster.inline0.dev/hu/docs/network/protocols)).

Mindhárom hálózati átvitelt indít, ezért `RuntimeException` hibát dob, ha a kezelőt `['processes' => false]` vagy `Pitmaster::processFreeOptions()` beállítással nyitották meg.

## Klónozás

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

Az új tároló mindig az SHA-1 értéket használja. Ha a klónozás nem sikerül, és a cél korábban nem létezett, a részben létrehozott könyvtár törlődik.

### A klónozás menete

1. **Inicializálás**: új, üres tároló létrehozása a célútvonalon
2. **Távoli tároló beállítása**: a URL mentése `remote.origin.url` néven, majd a lekérési refspec beállítása
3. **Hivatkozások felderítése**: `GET /info/refs?service=git-upload-pack` annak lekérdezésére, mi található a kiszolgálón
4. **Egyeztetés**: `want` sorok küldése minden távoli hivatkozáshoz (hash szerint duplikációmentesítve)
5. **Pack fogadása**: az összes kért objektumot tartalmazó pack fájl letöltése
6. **Pack írása**: a pack fájl mentése ide: `.git/objects/pack/`
7. **Pack indexelése**: az `.idx` fájl létrehozása a gyors kereséshez
8. **Hivatkozások beállítása**: távoli követő ágak (`refs/remotes/origin/*`) és címkék létrehozása
9. **HEAD beállítása**: a HEAD átállítása az alapértelmezett ágra (a kiszolgáló `symref=HEAD:...` képessége alapján, vagy `refs/heads/main` értékre, ha a kiszolgáló nem küld ilyet), majd az `branch.<name>.remote` és `branch.<name>.merge` beállítása
10. **Checkout**: az ág kijelentése, amely kiírja a munkafát és az indexet

A végső checkout során fellépő hibákat a rendszer elkapja és figyelmen kívül hagyja, ezért a klónozás üres vagy részleges munkafával is visszatérhet ([#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();
```

### A munkafa létrehozása

A pack fogadása után az Pitmaster meghívja az `$repo->checkout()` műveletet az alapértelmezett ágra. Ez a HEAD commit fájljait kiírja a munkakönyvtárba, és a fa bejegyzéseiből felépíti az indexet, így az `status()` tiszta munkafát jelez.

## Lekérés

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

### A lekérés menete

1. **Távoli URL beolvasása**: innen: `.git/config` (`remote.<name>.url`)
2. **Hivatkozások felderítése**: az összes távoli hivatkozás aktuális állapotának lekérése
3. **Szükséges és meglévő objektumok egyeztetése**:
   - Szükséges: azok a távoli hivatkozások, amelyekre a `remote.<name>.fetch` refspec illeszkedik (alapértelmezés szerint `+refs/heads/*:refs/remotes/<name>/*`), és amelyek objektumai hiányoznak helyben (mélység megadása esetén mindegyik)
   - Meglévő: az összes helyi hivatkozás végpontja
4. **Pack fogadása**: csak az új objektumok letöltése
5. **Pack írása**: mentés ide: `.git/objects/pack/`, az index elkészítésével együtt
6. **Követő hivatkozások frissítése**: minden illeszkedő távoli ág esetén az `refs/remotes/<remote>/<branch>` frissítése, majd minden meghirdetett címkehivatkozás kiírása. A címkeobjektumokat a rendszer nem kéri külön, így egy új, megjegyzésekkel ellátott címke olyan objektumra mutató hivatkozásként is kiírható, amelyet nem töltött le ([#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');
```

Az `resolve()` a neveket `refs/heads/<name>`, `refs/tags/<name>`, majd magát a nevet használva keresi, ezért a rövid `origin/main` forma nem található.

Az Pitmaster vékony packeket kér, amelyeket még nem tud indexelni ([#16](https://github.com/inline0/pitmaster/issues/16)). Ha egy kiszolgáló vékony packkel válaszol egy részleges lekérésre, az `fetch()` `PackParseException` hibát dob.

### Naprakész

Ha a távoli hivatkozások által megcélzott összes objektum már helyben is létezik, a rendszer nem kér pack fájlt, és az `fetch()` a hivatkozások felderítése után visszatér. Ebben az esetben a követő hivatkozások sem frissülnek, akkor sem, ha a távoli ágak már meglévő commitokra léptek tovább ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Feltöltés

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

### A feltöltés menete

1. **Távoli URL beolvasása**: innen: `.git/config`
2. **A feltöltendők meghatározása**: a helyi ág feloldása a commit hashére
3. **Távoli hivatkozások felderítése**: a távoli ág aktuális állapotának lekérése (a régi hash kiszámításához)
4. **Fast-forward ellenőrzése**: ha a távoli ág nem a helyi ág őse, az `RuntimeException("Push rejected: non-fast-forward update to <branch>")` még bármi elküldése előtt hibát dob
5. **Hivatkozásfrissítés küldése**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Packadatok küldése**: a távoli fél által igényelt objektumokat tartalmazó pack
7. **Eredmény ellenőrzése**: a kiszolgáló hivatkozásonként jelzi a sikert vagy a hibát

Az `pre-push` hook az frissítés elküldése előtt fut le, kivéve, ha a hookok ki vannak kapcsolva.

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

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

### A feltöltés követelményei

- Az aktuális ágnak helyben léteznie kell
- Egy távoli tárolóhoz be kell állítani egy URL értéket
- A távoli tárolónak el kell fogadnia a feltöltést (hitelesítés, jogosultságok, fast-forward szabályzatok)

### Kényszerített feltöltés

Pitmaster a távoli élő refhirdetésből küldi a régi hash-t, az `push()` pedig helyben elutasítja a nem gyorsított előrefrissítéseket. Távoli ág felülírásához használja a force-with-lease opciót:

```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 (fetch belső működése)

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

A want/have egyeztetés pkt-line kérést állít össze:

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

A szerver nyugtázásokkal válaszol, majd elküldi a pack-adatokat (esetleg side-band csomagolásban).

## ReceivePackClient (push belső működése)

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

A push-kérés formátuma:

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

## Hibakezelés

```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()` egyszerű `RuntimeException` kivételt (ismeretlen távoli tároló, folyamat nélküli fogantyú) vagy `PackParseException` kivételt (nem indexelhető pack) is dobhat. Az `ProtocolException` és az `PackParseException` egyaránt az `\RuntimeException` osztályból származik, ezért az `\RuntimeException` elkapása mindegyikre kiterjed.

## Távoli tárolók konfigurációja

A távoli tárolók itt vannak tárolva: `.git/config`:

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

Elérés a konfigurációs API segítségével:

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

## Pack-adatok push esetén

Jelenleg az Pitmaster összegyűjti a pusholt commitokból elérhető, de a távoli tároló aktuális refjeiből nem elérhető objektumokat, majd alapobjektumokként (delta-tömörítés nélkül) packba írja őket. Ez funkcionálisan helyes, de nagyobb packokat eredményez, mint az `git push`. A delta-tömörítés a pack írásakor tervezett optimalizálás.
