
# 프로토콜

Pitmaster은 git 서버와 통신하기 위한 여러 네트워크 전송 프로토콜을 지원합니다. Smart HTTP이 기본 전송 방식이며, 더 폭넓은 호환성을 위한 추가 프로토콜도 제공됩니다.

## Smart HTTP(기본)

권장 전송 방식입니다. HTTPS을 통해 작동하며 가장 널리 배포된 프로토콜입니다. 사용자 지정 헤더나 인증 정보를 위한 API은 없습니다. 인증 정보는 URL(`https://user:token@host/repo.git`)로만 전달할 수 있으며, PHP은 이를 Basic 인증으로 보냅니다. `clone()`은 인증 정보를 포함한 URL을 `.git/config`의 `remote.origin.url`로 저장합니다. 요청에는 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/ko/docs/network/smart-http)을 참조하세요.

### 프로토콜 v1

기존 smart HTTP 프로토콜입니다. 검색 중에 모든 ref를 전송하고 want/have/done 행으로 협상합니다.

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

### 프로토콜 v2

구조화된 명령(`ls-refs`, `fetch`)을 사용하는 최신 프로토콜입니다. 클라이언트가 ref 목록을 필터링할 수 있어 ref가 많은 저장소에서 더 효율적입니다.

`clone()` 및 `fetch()`은 항상 프로토콜 v1을 사용합니다. 프로토콜 v2은 `SmartHttpClient::discoverRefsV2()` 및 `UploadPackClient::fetchV2()`을 통한 smart HTTP에서 명시적으로 사용할 수 있습니다.

## 바보 HTTP

smart HTTP 프로토콜을 지원하지 않는 서버를 위한 대체 방식입니다. dumb 프로토콜은 일반 HTTP GET 요청으로 객체와 pack을 하나씩 가져옵니다.

```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`(사용 가능한 pack 목록)
4. `GET /objects/pack/pack-<hash>.pack` 및 `.idx`(목록에 있는 각 pack과 해당 인덱스)
5. `GET /objects/<xx>/<rest>`(ref 끝점에서 순회하는 개별 느슨한 객체)

5단계는 서버에 pack 목록이 없을 때만 실행됩니다. pack 목록이 있으면 서버에 느슨한 파일로만 존재하는 객체는 다운로드되지 않습니다([#18](https://github.com/inline0/pitmaster/issues/18)). dumb 클라이언트는 40자(SHA-1) 객체 ID만 허용합니다. smart HTTP 교환에 실패하면 `clone()` 및 `fetch()`는 dumb HTTP로 대체합니다.

## SSH

SSH 전송 방식은 로드된 경우 `ssh2` PHP 확장을 사용합니다. 그렇지 않으면 `proc_open()`를 통해 시스템 `ssh` 클라이언트를 시작합니다. 두 경우 모두 원격에서 `git-upload-pack` 또는 `git-receive-pack`을 실행합니다.

```php
use Pitmaster\Protocol\SshClient;

$client = new SshClient();
```

SSH URL:
```
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`로 인증을 시도하고, RSA 키가 있지만 거부된 경우 `~/.ssh/id_ed25519`으로 시도하며, 그 외에는 SSH 에이전트를 사용합니다. `PITMASTER_SSH_*` 변수는 무시되며 이 경로에서는 서버의 호스트 키를 확인하지 않습니다([#22](https://github.com/inline0/pitmaster/issues/22)).

SSH 전송 방식은 `git-upload-pack` 또는 `git-receive-pack`를 원격 명령으로 실행하고, smart HTTP과 동일한 pkt-line 프로토콜을 사용해 SSH 채널로 통신합니다.

## Git 프로토콜(TCP)

기본 git 프로토콜은 PHP 소켓을 사용해 TCP 포트 9418으로 통신합니다. `GitProtocolClient`은 ref 검색과 `upload-pack`(페치)를 지원하지만 푸시는 할 수 없습니다. `clone()` 및 `fetch()`는 `git://` URL을 이 프로토콜로 전달하지 않으므로 클라이언트를 직접 사용하세요.

```php
use Pitmaster\Protocol\GitProtocolClient;

$client = new GitProtocolClient();
```

Git URL:
```
git://github.com/user/repo.git
```

특징:
- 인증이 없습니다(실제로는 읽기 전용)
- 암호화되지 않습니다
- 대부분의 호스팅 제공업체에서 사용이 중단되었습니다
- smart HTTP과 동일한 pkt-line 프레이밍을 사용합니다

git 프로토콜은 초기 요청으로 서비스 이름과 저장소 경로를 전송한 다음, 표준 pkt-line ref 검색 및 pack 협상을 진행합니다.

## 번들

Git 번들은 ref 정보가 포함된 오프라인 pack 파일입니다. 네트워크 연결 없이 저장소 데이터를 전송할 수 있습니다.

```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()`으로 인덱스를 만든 다음 직접 ref를 업데이트하세요.

번들 형식:
```
# 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으로 대체 시도 |

`git://` URL 및 로컬 경로는 이 메서드에서 지원되지 않습니다. smart HTTP 클라이언트로 전달되어 실패합니다. `git://`에는 `GitProtocolClient`를 직접 사용하세요. 사용자 이름과 포트를 모두 포함하는 HTTPS URL(`https://user@host:8443/repo.git`)는 현재 SSH URL([#17](https://github.com/inline0/pitmaster/issues/17))로 잘못 인식됩니다.
