
# プロトコル

Pitmasterはgitサーバーとの通信に複数のネットワークトランスポートプロトコルをサポートします。スマートHTTPが主要なトランスポートです。互換性を広げるため、ほかのプロトコルも利用できます。

## スマート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');
```

詳しくは[スマートHTTP](https://pitmaster.inline0.dev/ja/docs/network/smart-http)を参照してください。

### プロトコルv1

従来のスマートHTTPプロトコルです。検出時にすべてのrefが送信され、ネゴシエーションではwant/have/done行を使用します。

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

### プロトコルv2

構造化コマンド（`ls-refs`、`fetch`）を備えた新しいプロトコルです。クライアントがref一覧を絞り込めるため、refの多いリポジトリでより効率的です。

`clone()`と`fetch()`は常にプロトコルv1を使用します。プロトコルv2は、`SmartHttpClient::discoverRefsV2()`と`UploadPackClient::fetchV2()`経由のスマートHTTPで明示的に利用できます。

## ダムHTTP

スマートHTTPプロトコルに対応していないサーバー向けのフォールバックです。ダムプロトコルでは、通常のHTTP GETリクエストを使ってオブジェクトやpackを個別に取得します。

```php
use Pitmaster\Protocol\DumbHttpClient;

$client = new DumbHttpClient();
```

ダムHTTPの制限：
- ネゴシエーションなし（クライアントはすべてをダウンロードする必要があります）
- 転送中のデルタ圧縮なし
- スマートHTTPより大幅に低速
- 一部のサーバー（GitHub、GitLab）は非対応

ダムプロトコルでは次を取得します。
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を1つも提示しない場合にのみ実行されます。packが提示される場合、サーバー上にルースファイルとしてのみ存在するオブジェクトはダウンロードされません（[#18](https://github.com/inline0/pitmaster/issues/18)）。ダムクライアントが受け付けるオブジェクトIDは40文字（SHA-1）のみです。スマートHTTPの通信に失敗すると、`clone()`と`fetch()`はダムHTTPにフォールバックします。

## SSH

SSHトランスポートでは、PHP拡張機能が読み込まれている場合は`ssh2`を使用します。そうでない場合は、`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
```

拡張機能は不要です。2つの方法では動作が異なります。

**システムの`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`を実行し、スマート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
```

特徴：
- 認証なし（実質読み取り専用）
- 暗号化なし
- ほとんどのホスティングプロバイダーで非推奨
- スマート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()`はこのファイルを直接使用します。

## プロトコルの比較

| プロトコル | 認証 | 暗号化 | プッシュ | 主な用途 |
|----------|------|-----------|------|-------------|
| スマートHTTP | URL内の認証情報 | HTTPS | 可 | 主要トランスポート |
| ダムHTTP | URL内の認証情報 | HTTPS | 不可 | 従来方式のフォールバック |
| SSH | SSHキーまたはエージェント | SSH | 可 | プライベートリポジトリ、CI |
| Git（TCP） | なし | なし | 不可 | 読み取り専用ミラー |
| バンドル | 該当なし | 該当なし | 該当なし | オフライン転送 |

## トランスポートの選択

`Pitmaster::clone()`、`$repo->fetch()`、`$repo->push()`はURLからトランスポートを選択します。

| URLパターン | トランスポート |
|------------|-----------|
| `ssh://...` | SSH |
| `user@host:path` | SSH |
| その他（`https://...`、`http://...`） | スマートHTTP。cloneとfetchはダムHTTPにフォールバック |

`git://` URLとローカルパスはこれらのメソッドではサポートされていません。スマートHTTPクライアントに渡され、失敗します。`git://`には`GitProtocolClient`を直接使用してください。ユーザーとポートの両方を含むHTTPSURL（`https://user@host:8443/repo.git`）は、現在SSHURLと誤認されます（[#17](https://github.com/inline0/pitmaster/issues/17)）。
