
# Протоколи

Pitmaster поддържа няколко мрежови транспортни протокола за комуникация със сървъри на git. Smart HTTP е основният транспорт. Налични са и допълнителни протоколи за по-широка съвместимост.

## Smart HTTP (основен)

Препоръчителният транспорт. Работи през HTTPS и е най-широко разпространеният протокол. Няма API за персонализирани заглавки или идентификационни данни; те могат да се подават само в URL (`https://user:token@host/repo.git`), което PHP изпраща с Basic удостоверяване. `clone()` съхранява URL, включително идентификационните данни, като `remote.origin.url` в `.git/config`. Заявките използват поточните обвивки на PHP, затова `allow_url_fopen` трябва да е активирано.

```php
use Pitmaster\Protocol\SmartHttpClient;

$http = new SmartHttpClient(timeout: 30);
$discovery = $http->discoverRefs('https://github.com/user/repo.git');
```

Вижте [Smart HTTP](https://pitmaster.inline0.dev/bg/docs/network/smart-http) за пълни подробности.

### Протокол v1

Традиционният протокол smart HTTP. При откриването се изпращат всички препратки, а договарянето използва редове want/have/done.

```php
use Pitmaster\Protocol\ProtocolV1;
```

### Протокол v2

По-новият протокол със структурирани команди (`ls-refs`, `fetch`). По-ефективен е за хранилища с много препратки, защото клиентът може да филтрира списъка с препратки.

`clone()` и `fetch()` винаги използват протокол v1. Протокол v2 е наличен изрично през smart HTTP чрез `SmartHttpClient::discoverRefsV2()` и `UploadPackClient::fetchV2()`.

## Тъп HTTP

Резервен вариант за сървъри, които не поддържат протокола smart HTTP. Dumb протоколът извлича обекти и пакети поотделно чрез обикновени HTTP GET заявки.

```php
use Pitmaster\Protocol\DumbHttpClient;

$client = new DumbHttpClient();
```

Ограничения на Dumb HTTP:
- Няма договаряне (клиентът трябва да изтегли всичко)
- Няма делта компресия по време на прехвърлянето
- Значително по-бавен е от smart HTTP
- Някои сървъри (GitHub, GitLab) не го поддържат

Dumb протоколът извлича:
1. `GET /info/refs` (обикновен текст, без pkt-line)
2. `GET /HEAD` (клонът по подразбиране)
3. `GET /objects/info/packs` (списък с наличните пакети)
4. `GET /objects/pack/pack-<hash>.pack` и `.idx` (всеки посочен пакет и неговият индекс)
5. `GET /objects/<xx>/<rest>` (отделни свободни обекти, обходени от върховете на препратките)

Стъпка 5 се изпълнява само когато сървърът не посочва пакети. Когато посочи пакети, обектите, които съществуват на сървъра само като отделни свободни файлове, не се изтеглят ([#18](https://github.com/inline0/pitmaster/issues/18)). Dumb клиентът приема само идентификатори на обекти с дължина 40 знака (SHA-1). `clone()` и `fetch()` преминават към dumb HTTP, ако обменът по smart HTTP е неуспешен.

## SSH

Транспортът SSH използва разширението `ssh2` PHP, когато то е заредено. В противен случай стартира системния клиент `ssh` чрез `proc_open()`. И в двата случая изпълнява `git-upload-pack` или `git-receive-pack` на отдалечената машина.

```php
use Pitmaster\Protocol\SshClient;

$client = new SshClient();
```

URL адреси на SSH:
```
git@github.com:user/repo.git
ssh://git@github.com/user/repo.git
```

Не е необходимо разширение. Двата начина работят различно:

**Системен клиент `ssh`** (използва се, когато `ssh2` не е заредено). Pitmaster изпълнява `ssh -o BatchMode=yes -o ConnectTimeout=30 -p <port> <user>@<host> <command>`, така че удостоверяването и проверката на ключа на хоста следват конфигурацията и агента на вашето SSH. Тези променливи на средата настройват командата:

| Променлива | Действие |
|----------|--------|
| `PITMASTER_SSH_COMMAND` | Изпълним файл, който да се стартира вместо `ssh` |
| `PITMASTER_SSH_IDENTITY_FILE` | Подавана като `-i <file>` |
| `PITMASTER_SSH_KNOWN_HOSTS` | Подавана като `-o UserKnownHostsFile=<file>` |
| `PITMASTER_SSH_STRICT_HOST_KEY_CHECKING` | Подавана като `-o StrictHostKeyChecking=<value>` |

Този начин стартира процес и затова не е наличен за манипулатори, отворени с `['processes' => false]`.

**Разширение `ssh2`** (винаги с предимство, когато е заредено). Pitmaster се удостоверява с `~/.ssh/id_rsa`, след това с `~/.ssh/id_ed25519`, ако RSA ключът съществува, но е отхвърлен, и в противен случай с агента SSH. Променливите `PITMASTER_SSH_*` се игнорират, а ключът на хоста на сървъра не се проверява при този начин ([#22](https://github.com/inline0/pitmaster/issues/22)).

Транспортът SSH изпълнява `git-upload-pack` или `git-receive-pack` като отдалечена команда и комуникира през канала SSH, като използва същия протокол pkt-line като smart HTTP.

## Протокол Git (TCP)

Родният протокол на git комуникира през TCP порт 9418, използвайки сокети PHP. `GitProtocolClient` реализира откриването на препратки и `upload-pack` (извличане); не може да изпраща промени. `clone()` и `fetch()` не насочват URL адреси `git://` към него, затова използвайте клиента директно.

```php
use Pitmaster\Protocol\GitProtocolClient;

$client = new GitProtocolClient();
```

URL адреси на Git:
```
git://github.com/user/repo.git
```

Характеристики:
- Няма удостоверяване (на практика е само за четене)
- Няма криптиране
- Повечето доставчици на хостинг вече не го поддържат
- Използва същото рамкиране pkt-line като smart HTTP

Протоколът git изпраща името на услугата и пътя до хранилището като първоначална заявка, след което продължава със стандартното откриване на препратки и договаряне на пакети чрез pkt-line.

## Пакети

Пакетите Git са офлайн пакетни файлове с информация за препратките. Те позволяват прехвърляне на данни за хранилище без мрежова връзка.

```php
use Pitmaster\Protocol\Bundle;

// Read a bundle file
$bundle = Bundle::open('/path/to/repo.bundle');
$refs = $bundle->refs();
$packData = $bundle->packData();
$prerequisites = $bundle->prerequisites();

// Create one
$bundle = Bundle::create(['refs/heads/main' => $commitId], $packData);
$bundle->writeTo('/path/to/out.bundle');
```

Pitmaster чете заглавките на пакети v2 и v3. Няма метод `Repository` за импортиране на пакет: запишете `packData()` в `.git/objects/pack/`, изградете индекса му с `PackIndexer::writeIndex()` и актуализирайте препратките сами.

Формат на пакетите:
```
# v2 git bundle\n
-<prerequisite hash>\n    (zero or more)
<hash> <refname>\n        (one or more)
\n
<pack data>               (standard pack file format)
```

Пакетите са полезни за:
- Прехвърляне на данни чрез физически носител (USB устройства)
- Инкрементални резервни копия
- Подготвяне на клонинги в изолирани от мрежата среди

## Плитки клонинги

Pitmaster поддържа плитки клонинги, при които се извлича само ограничен брой комити от историята.

```php
use Pitmaster\Protocol\ShallowClone;
```

Подайте дълбочина на `Pitmaster::clone($url, $path, $depth)` или `$repo->fetch($remote, $depth)`. Заявката добавя възможността `shallow` и ред `deepen`:
```
want <hash>
deepen <depth>
```

Сървърът връща редове `shallow <hash>`, които указват къде е прекъсната историята. Pitmaster изчислява плитката граница въз основа на самата дълбочина и използва редовете `shallow` на сървъра, когато това изчисление не намира такава; резултатът се съхранява в `.git/shallow`. `ShallowClone::readShallow()`, `writeShallow()` и `isShallow()` работят директно с този файл.

## Сравнение на протоколите

| Протокол | Удостоверяване | Криптиране | Изпращане | Типична употреба |
|----------|------|-----------|------|-------------|
| Smart HTTP | Идентификационни данни в URL | HTTPS | Да | Основен транспорт |
| Dumb HTTP | Идентификационни данни в URL | HTTPS | Не | Резервен вариант за стари системи |
| SSH | Ключове или агент SSH | SSH | Да | Частни хранилища, CI |
| Git (TCP) | Няма | Няма | Не | Огледални копия само за четене |
| Bundle | Не се прилага | Не се прилага | Не се прилага | Офлайн прехвърляне |

## Избор на транспорт

`Pitmaster::clone()`, `$repo->fetch()` и `$repo->push()` избират транспорта според URL:

| Шаблон URL | Транспорт |
|------------|-----------|
| `ssh://...` | SSH |
| `user@host:path` | SSH |
| Всичко останало (`https://...`, `http://...`) | Smart HTTP; clone и fetch преминават към dumb HTTP |

URL адресите `git://` и локалните пътища не се поддържат от тези методи: изпращат се към клиента smart HTTP и операцията се проваля. За `git://` използвайте директно `GitProtocolClient`. HTTPS URL, който съдържа едновременно потребител и порт (`https://user@host:8443/repo.git`), в момента погрешно се приема за SSH URL ([#17](https://github.com/inline0/pitmaster/issues/17)).
