
# Klona, hämta, skicka

Pitmaster stöder de tre grundläggande nätverksåtgärderna i git: klona (första nedladdningen), hämta (inkrementell nedladdning) och skicka (uppladdning). URL:er av typen SSH (`ssh://...`, `user@host:path`) använder transporten SSH; övriga URL:er använder smart HTTP, och kloning och hämtning faller tillbaka på dumb HTTP när den smarta kommunikationen misslyckas (se [Protokoll](https://pitmaster.inline0.dev/sv/docs/network/protocols)).

Alla tre startar nätverkstransporter och ger därför `RuntimeException` vid användning av ett handtag som öppnats med `['processes' => false]` eller `Pitmaster::processFreeOptions()`.

## Klona

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

Det nya repot använder alltid SHA-1. Om kloningen misslyckas och målkatalogen inte fanns sedan tidigare tas den delvis skapade katalogen bort.

### Kloningsflöde

1. **Initiera**: skapa ett nytt tomt repo på målsökvägen
2. **Konfigurera fjärrserver**: spara URL som `remote.origin.url` och konfigurera fetch-refspecen
3. **Upptäck referenser**: `GET /info/refs?service=git-upload-pack` för att ta reda på vad servern har
4. **Förhandla**: skicka rader med `want` för alla fjärrreferenser (deduplicerade efter hash)
5. **Ta emot pack**: ladda ned pack-filen som innehåller alla begärda objekt
6. **Skriv pack**: spara pack-filen i `.git/objects/pack/`
7. **Indexera pack**: generera filen `.idx` för snabba uppslagningar
8. **Konfigurera referenser**: skapa fjärrspårningsgrenar (`refs/remotes/origin/*`) och taggar
9. **Ange HEAD**: peka HEAD på standardgrenen (från serverns funktion `symref=HEAD:...`, eller `refs/heads/main` om servern inte skickar någon) och ange `branch.<name>.remote` och `branch.<name>.merge`
10. **Checka ut**: checka ut grenen, vilket skriver arbetsträdet och indexet

Fel vid den avslutande utcheckningen fångas upp och ignoreras, så en kloning kan slutföras med ett tomt eller ofullständigt arbetsträd ([#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();
```

### Skapa arbetsträdet

När pack-filen har tagits emot anropar Pitmaster `$repo->checkout()` för standardgrenen. Det skriver varje fil från HEAD-committens träd till arbetskatalogen och skapar indexet utifrån trädposterna, så att `status()` visar ett rent arbetsträd.

## Hämta

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

### Hämtningsflöde

1. **Läs fjärrserverns URL**: från `.git/config` (`remote.<name>.url`)
2. **Upptäck referenser**: hämta aktuellt tillstånd för alla fjärrreferenser
3. **Förhandla om wants/haves**:
   - Want: fjärrreferenser som matchas av refspecarna `remote.<name>.fetch` (standardvärde `+refs/heads/*:refs/remotes/<name>/*`) och vars objekt saknas lokalt (alla när ett djup anges)
   - Have: alla lokala referenstoppar
4. **Ta emot pack**: ladda ned endast de nya objekten
5. **Skriv pack**: spara i `.git/objects/pack/` och skapa dess index
6. **Uppdatera spårningsreferenser**: uppdatera `refs/remotes/<remote>/<branch>` för varje matchad fjärrgren och skriv varje annonserad taggreferens. Taggobjekt begärs inte separat, så en ny annoterad tagg kan skrivas som en referens till ett objekt som inte har laddats ned ([#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()` slår upp namn som `refs/heads/<name>`, `refs/tags/<name>` och själva namnet, så den korta formen `origin/main` hittas inte.

Pitmaster begär tunna pack-filer, som ännu inte kan indexeras ([#16](https://github.com/inline0/pitmaster/issues/16)). Om en server svarar på en inkrementell hämtning med en tunn pack-fil gör det att `fetch()` ger `PackParseException`.

### Redan uppdaterat

Om alla objekt som fjärrreferenserna pekar på redan finns lokalt begärs ingen pack-fil och `fetch()` returnerar efter referensupptäckten. Då uppdateras inte heller spårningsreferenserna, även om fjärrgrenarna har flyttats till commits som du redan har ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Skicka

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

### Skickningsflöde

1. **Läs fjärrserverns URL**: från `.git/config`
2. **Avgör vad som ska skickas**: slå upp den lokala grenen till dess commit-hash
3. **Upptäck fjärrreferenser**: hämta fjärrgrenens aktuella tillstånd (för att beräkna den gamla hashen)
4. **Kontrollera snabbspolning**: om fjärrgrenen inte är en förfader till den lokala grenen, ge `RuntimeException("Push rejected: non-fast-forward update to <branch>")` innan något skickas
5. **Skicka referensuppdatering**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Skicka packdata**: pack som innehåller objekten fjärrservern behöver
7. **Kontrollera resultatet**: servern svarar med status för varje referens

Hooken `pre-push` körs innan uppdateringen skickas, såvida hooks inte är inaktiverade.

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

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

### Krav för att skicka

- Den aktuella grenen måste finnas lokalt
- En fjärrserver måste vara konfigurerad med en URL
- Fjärrservern måste godkänna skickningen (autentisering, behörigheter, regler för uppdateringar som inte är snabbspolningar)

### Tvingad skickning

Pitmaster skickar den gamla hashen från fjärrserverns aktuella referensannonsering, och `push()` avvisar lokalt uppdateringar som inte är snabbspolningar. Om du vill skriva över en fjärrgren använder du 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 (interna detaljer för hämtning)

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

Förhandlingen om want/have bygger en pkt-line-begäran:

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

Servern svarar med bekräftelser och därefter packdata (eventuellt med sidobandsinkapsling).

## ReceivePackClient (interna detaljer för skickning)

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

Formatet för skickningsbegäran:

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

## Felhantering

```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()` kan också ge ett vanligt `RuntimeException` (okänd fjärrserver, handtag utan processer) eller `PackParseException` (pack som inte kan indexeras). Både `ProtocolException` och `PackParseException` ärver från `\RuntimeException`, så om du fångar `\RuntimeException` täcks alla dessa fall.

## Konfiguration av fjärrservrar

Fjärrservrar lagras i `.git/config`:

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

Åtkomst via konfigurationens API:

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

## Packdata för skickning

För närvarande samlar Pitmaster in de objekt som kan nås från commitarna som skickas och som inte kan nås från fjärrserverns aktuella referenser, och skriver dem som basobjekt (utan deltakomprimering) till en pack-fil. Detta är funktionellt korrekt men skapar större pack-filer än `git push`. Deltakomprimering vid skrivning av pack-filer är en planerad optimering.
