
# Clonar, obtener y enviar

Pitmaster admite las tres operaciones de red fundamentales de git: clonar (descarga inicial), obtener (descarga incremental) y enviar (subida). Las URL SSH (`ssh://...`, `user@host:path`) usan el transporte SSH; las demás URL usan HTTP inteligente, y la clonación y la obtención recurren a HTTP simple si falla el intercambio inteligente (consulte [Protocolos](https://pitmaster.inline0.dev/es/docs/network/protocols)).

Las tres operaciones inician transportes de red, por lo que generan `RuntimeException` en un identificador abierto con `['processes' => false]` o `Pitmaster::processFreeOptions()`.

## Clonar

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

El nuevo repositorio siempre usa SHA-1. Si la clonación falla y el destino no existía antes, se elimina el directorio creado parcialmente.

### Proceso de clonación

1. **Inicializar**: crear un repositorio nuevo y vacío en la ruta de destino
2. **Configurar el remoto**: guardar URL como `remote.origin.url` y configurar la especificación de referencia para la obtención
3. **Descubrir referencias**: `GET /info/refs?service=git-upload-pack` para averiguar qué contiene el servidor
4. **Negociar**: enviar líneas `want` para todas las referencias remotas (deduplicadas por hash)
5. **Recibir el paquete**: descargar el archivo de paquete que contiene todos los objetos solicitados
6. **Escribir el paquete**: guardar el archivo de paquete en `.git/objects/pack/`
7. **Indexar el paquete**: generar el archivo `.idx` para agilizar las búsquedas
8. **Configurar las referencias**: crear ramas de seguimiento remoto (`refs/remotes/origin/*`) y etiquetas
9. **Establecer HEAD**: apuntar HEAD a la rama predeterminada (según la capacidad `symref=HEAD:...` del servidor, o `refs/heads/main` si el servidor no envía ninguna) y establecer `branch.<name>.remote` y `branch.<name>.merge`
10. **Cambiar de rama**: cambiar a esa rama, lo que escribe el árbol de trabajo y el índice

Los errores durante el cambio de rama final se capturan y se ignoran, por lo que la clonación puede devolver un árbol de trabajo vacío o parcial ([#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();
```

### Creación del árbol de trabajo

Después de recibir el paquete, Pitmaster llama a `$repo->checkout()` para la rama predeterminada. Esto escribe todos los archivos del árbol de la confirmación HEAD en el directorio de trabajo y crea el índice a partir de las entradas del árbol, de modo que `status()` muestra un árbol de trabajo limpio.

## Obtener

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

### Proceso de obtención

1. **Leer el remoto URL**: desde `.git/config` (`remote.<name>.url`)
2. **Descubrir referencias**: obtener el estado actual de todas las referencias remotas
3. **Negociar lo que se necesita y lo que se tiene**:
   - Se necesita: referencias remotas que coincidan con las especificaciones `remote.<name>.fetch` (predeterminadas `+refs/heads/*:refs/remotes/<name>/*`) cuyos objetos falten localmente (todas cuando se indique una profundidad)
   - Se tiene: cada punta de referencia local
4. **Recibir el paquete**: descargar solo los objetos nuevos
5. **Escribir el paquete**: guardar en `.git/objects/pack/` y crear su índice
6. **Actualizar las referencias de seguimiento**: actualizar `refs/remotes/<remote>/<branch>` para cada rama remota coincidente y escribir cada referencia de etiqueta anunciada. Los objetos de etiqueta no se solicitan por separado, por lo que una etiqueta anotada nueva puede escribirse como referencia a un objeto que no se descargó ([#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()` busca los nombres como `refs/heads/<name>`, `refs/tags/<name>` y el propio nombre, por lo que no encuentra la forma abreviada `origin/main`.

Pitmaster solicita paquetes delgados, que todavía no puede indexar ([#16](https://github.com/inline0/pitmaster/issues/16)). Si un servidor responde a una obtención incremental con un paquete delgado, `fetch()` genera `PackParseException`.

### Ya está actualizado

Si todos los objetos a los que apuntan las referencias remotas ya existen localmente, no se solicita ningún paquete y `fetch()` termina después de descubrir las referencias. En ese caso tampoco se actualizan las referencias de seguimiento, aunque las ramas remotas hayan avanzado a confirmaciones que ya tiene ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Enviar

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

### Proceso de envío

1. **Leer el remoto URL**: desde `.git/config`
2. **Determinar qué enviar**: resolver la rama local para obtener su hash de confirmación
3. **Descubrir las referencias remotas**: obtener el estado actual de la rama remota (para calcular el hash anterior)
4. **Comprobar el avance rápido**: si la rama remota no es antecesora de la rama local, generar `RuntimeException("Push rejected: non-fast-forward update to <branch>")` antes de enviar nada
5. **Enviar la actualización de referencia**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Enviar los datos del paquete**: paquete con los objetos que necesita el remoto
7. **Comprobar el resultado**: el servidor responde con el éxito o el fallo de cada referencia

El hook `pre-push` se ejecuta antes de enviar la actualización, salvo que los hooks estén desactivados.

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

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

### Requisitos para enviar

- La rama actual debe existir localmente
- Debe haber un remoto configurado con un URL
- El remoto debe aceptar el envío (autenticación, permisos y políticas de avance no rápido)

### Envío forzado

Pitmaster envía el hash anterior del anuncio de referencias activas del remoto y `push()` rechaza localmente las actualizaciones que no sean de avance rápido. Para sobrescribir una rama remota, use force-with-lease:

```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 (detalles internos de la obtención)

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

La negociación de lo que se necesita y lo que se tiene crea una solicitud pkt-line:

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

El servidor responde con acuses de recibo y luego con los datos del paquete (posiblemente encapsulados en side-band).

## ReceivePackClient (detalles internos del envío)

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

Formato de la solicitud de envío:

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

## Gestión de errores

```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()` también puede generar un `RuntimeException` simple (remoto desconocido, identificador sin procesos) o `PackParseException` (un paquete que no se puede indexar). Tanto `ProtocolException` como `PackParseException` extienden `\RuntimeException`, así que capturar `\RuntimeException` los cubre a todos.

## Configuración del remoto

Los remotos se almacenan en `.git/config`:

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

Acceso mediante la API de configuración:

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

## Datos del paquete para el envío

Actualmente, Pitmaster recopila los objetos accesibles desde las confirmaciones que se envían y que no lo son desde las referencias actuales del remoto, y los escribe como objetos base (sin compresión delta) en un paquete. Esto es funcionalmente correcto, pero genera paquetes más grandes que `git push`. La compresión delta al escribir paquetes es una optimización prevista.
