
# Klon, hent, push

Pitmaster understøtter de tre grundlæggende git-netværkshandlinger: clone (første download), fetch (løbende download) og push (upload). URL'er med SSH (`ssh://...`, `user@host:path`) bruger transporten SSH; andre URL'er bruger smart HTTP, og clone og fetch falder tilbage til dumb HTTP, hvis den smarte udveksling mislykkes (se [Protokoller](https://pitmaster.inline0.dev/da/docs/network/protocols)).

Alle tre starter netværkstransport, så de kaster `RuntimeException` på et handle åbnet med `['processes' => false]` eller `Pitmaster::processFreeOptions()`.

## Klon

```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 nye repository bruger altid SHA-1. Hvis kloningen mislykkes, og destinationen ikke fandtes i forvejen, fjernes den delvist oprettede mappe.

### Kloningsforløb

1. **Initialisér**: Opret et nyt, tomt repository på destinationsstien
2. **Konfigurér fjernrepository**: Gem URL som `remote.origin.url`, og opsæt fetch-refspec'en
3. **Find refs**: `GET /info/refs?service=git-upload-pack` for at finde ud af, hvad serveren har
4. **Forhandl**: Send `want`-linjer for alle eksterne refs (dubletter fjernes ud fra hash)
5. **Modtag pack**: Download pack-filen med alle de ønskede objekter
6. **Skriv pack**: Gem pack-filen i `.git/objects/pack/`
7. **Indeksér pack**: Generér filen `.idx` til hurtige opslag
8. **Opsæt refs**: Opret branches til fjernsporing (`refs/remotes/origin/*`) og tags
9. **Angiv HEAD**: Lad HEAD pege på standardbranchen (fra serverens funktion `symref=HEAD:...` eller `refs/heads/main`, hvis serveren ikke sender nogen), og angiv `branch.<name>.remote` og `branch.<name>.merge`
10. **Check ud**: Check branchen ud, så working tree og indeks skrives

Fejl under den afsluttende checkout fanges og ignoreres, så et clone kan returnere med et tomt eller delvist working tree ([#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();
```

### Materialisering af working tree

Når pack-filen er modtaget, kalder Pitmaster `$repo->checkout()` for standardbranchen. Det skriver hver fil i HEAD-committets tree til arbejdsbiblioteket og opbygger indekset ud fra tree-posterne, så `status()` rapporterer et rent working tree.

## Hent

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

### Hentningsforløb

1. **Læs eksternt URL**: fra `.git/config` (`remote.<name>.url`)
2. **Find refs**: Hent den aktuelle tilstand for alle eksterne refs
3. **Forhandl ønsker/har**:
   - Ønskes: Eksterne refs, der matches af refspecs `remote.<name>.fetch` (standard `+refs/heads/*:refs/remotes/<name>/*`), og hvis objekter mangler lokalt (alle ved angivet dybde)
   - Har: Hvert lokalt ref-tip
4. **Modtag pack**: Download kun de nye objekter
5. **Skriv pack**: Gem i `.git/objects/pack/`, og opbyg indekset
6. **Opdatér refs til fjernsporing**: Opdatér `refs/remotes/<remote>/<branch>` for hver matchet ekstern branch, og skriv alle annoncerede tag-refs. Tag-objekter anmodes ikke separat, så et nyt annoteret tag kan blive skrevet som en ref til et objekt, der ikke blev downloadet ([#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 navne op som `refs/heads/<name>`, `refs/tags/<name>` og selve navnet, så kortformen `origin/main` ikke findes.

Pitmaster anmoder om tynde packs, som den endnu ikke kan indeksere ([#16](https://github.com/inline0/pitmaster/issues/16)). Hvis en server svarer på en inkrementel hentning med en tynd pack, får `fetch()` en `PackParseException`-fejl.

### Allerede opdateret

Hvis alle objekter, som de eksterne refs peger på, allerede findes lokalt, anmodes der ikke om nogen pack, og `fetch()` returnerer efter opslag af refs. I så fald opdateres refs til fjernsporing heller ikke, selv hvis de eksterne branches er flyttet til commits, du allerede har ([#23](https://github.com/inline0/pitmaster/issues/23)).

## Push

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

### Push-forløb

1. **Læs fjernlageret URL**: fra `.git/config`
2. **Find ud af, hvad der skal sendes**: opløs den lokale gren til dens commit-hash
3. **Find fjernlagerets referencer**: hent den aktuelle tilstand for fjernlagerets gren (for at beregne den gamle hash)
4. **Kontrollér fast-forward**: hvis fjernlagerets gren ikke er en forfader til den lokale gren, skal `RuntimeException("Push rejected: non-fast-forward update to <branch>")` kastes, før noget sendes
5. **Send referenceopdatering**: `<old-hash> <new-hash> refs/heads/<branch>`
6. **Send pakkedata**: pakke med de objekter, fjernlageret har brug for
7. **Kontrollér resultatet**: serveren svarer med succes eller fejl for hver reference

Hooken `pre-push` køres, før opdateringen sendes, medmindre hooks er deaktiveret.

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

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

### Krav til push

- Den aktuelle gren skal findes lokalt
- Et fjernlager skal være konfigureret med en URL
- Fjernlageret skal acceptere push (godkendelse, tilladelser og politikker for non-fast-forward)

### Force push

Pitmaster sender den gamle hash fra fjernlagerets aktuelle referenceannoncering, og `push()` afviser lokalt opdateringer, der ikke er fast-forward. Brug force-with-lease for at overskrive en gren på fjernlageret:

```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 (interne fetch-detaljer)

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

Forhandlingen om want/have opbygger en pkt-line-anmodning:

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

Serveren svarer med bekræftelser og derefter pakkedataene (muligvis indpakket i side-band).

## ReceivePackClient (interne push-detaljer)

```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 på push-anmodningen:

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

## Fejlhåndtering

```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 også kaste en almindelig `RuntimeException` (ukendt fjernlager, handle uden proces) eller `PackParseException` (en pakke, der ikke kan indekseres). Både `ProtocolException` og `PackParseException` udvider `\RuntimeException`, så du kan fange dem alle med `\RuntimeException`.

## Konfiguration af fjernlagre

Fjernlagre gemmes i `.git/config`:

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

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

## Pakkedata til push

I øjeblikket indsamler Pitmaster de objekter, der kan nås fra de commits, der sendes, men ikke fra fjernlagerets aktuelle referencer, og skriver dem som basisobjekter (uden deltacomprimering) i en pakke. Det fungerer korrekt, men giver større pakker end `git push`. Deltakomprimering ved pakkeskrivning er en planlagt optimering.
