Preskočiť na obsah

Klonovanie, načítanie, odosielanie

Pitmaster podporuje tri základné sieťové operácie git: klonovanie (počiatočné stiahnutie), načítanie (priebežné stiahnutie) a odosielanie (nahrávanie). Adresy URL SSH (ssh://..., user@host:path) používajú prenos SSH; ostatné adresy URL používajú inteligentný HTTP a klonovanie aj načítanie pri zlyhaní inteligentnej výmeny prejdú na jednoduchý HTTP (pozrite si Protokoly).

Všetky tri spúšťajú sieťové prenosy, preto pri objekte otvorenom pomocou ['processes' => false] alebo Pitmaster::processFreeOptions() vyhodia výnimku RuntimeException.

Klonovanie

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

Nový repozitár vždy používa SHA-1. Ak klonovanie zlyhá a cieľ predtým neexistoval, čiastočne vytvorený adresár sa odstráni.

Priebeh klonovania

  1. Inicializácia: vytvorte nový prázdny repozitár v cieľovej ceste
  2. Konfigurácia vzdialeného repozitára: uložte URL ako remote.origin.url a nastavte špecifikáciu odkazov na načítanie
  3. Zistenie odkazov: použite GET /info/refs?service=git-upload-pack na zistenie obsahu servera
  4. Dohodnutie: odošlite riadky want pre všetky vzdialené odkazy (hashy sa deduplikujú)
  5. Prijatie balíka: stiahnite súbor balíka obsahujúci všetky požadované objekty
  6. Zápis balíka: uložte súbor balíka do .git/objects/pack/
  7. Indexovanie balíka: vytvorte súbor .idx na rýchle vyhľadávanie
  8. Nastavenie odkazov: vytvorte vetvy sledujúce vzdialený repozitár (refs/remotes/origin/*) a značky
  9. Nastavenie HEAD: nasmerujte HEAD na predvolenú vetvu (podľa schopnosti symref=HEAD:... servera alebo na refs/heads/main, ak server žiadnu neposkytne) a nastavte branch.<name>.remote a branch.<name>.merge
  10. Checkout: vykonajte checkout tejto vetvy; tým sa zapíše pracovný strom aj index

Chyby počas záverečného checkoutu sa zachytia a ignorujú, takže klonovanie sa môže skončiť s prázdnym alebo čiastočným pracovným stromom (#19).

PHP
$repo = Pitmaster::clone($url, $path);
$head = $repo->head();
echo $head->message;

$branches = $repo->branches();
$files = $repo->status();

Vytvorenie pracovného stromu

Po prijatí balíka Pitmaster zavolá $repo->checkout() pre predvolenú vetvu. Tým sa zapíšu všetky súbory zo stromu odovzdania HEAD do pracovného adresára a z položiek stromu sa vytvorí index, takže status() oznámi čistý pracovný strom.

Načítanie

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

Priebeh načítania

  1. Čítanie vzdialeného URL: z .git/config (remote.<name>.url)
  2. Zistenie odkazov: zistite aktuálny stav všetkých vzdialených odkazov
  3. Dohodnutie požadovaných a dostupných objektov:
    • Požadované: vzdialené odkazy zodpovedajúce špecifikáciám odkazov remote.<name>.fetch (predvolené +refs/heads/*:refs/remotes/<name>/*), ktorých objekty lokálne chýbajú (pri zadanej hĺbke všetky)
    • Dostupné: vrcholový objekt každej lokálnej referencie
  4. Prijatie balíka: stiahnite iba nové objekty
  5. Zápis balíka: uložte ho do .git/objects/pack/ a vytvorte jeho index
  6. Aktualizácia sledovaných odkazov: aktualizujte refs/remotes/<remote>/<branch> pre každú zodpovedajúcu vzdialenú vetvu a zapíšte všetky oznámené odkazy značiek. Objekty značiek sa nevyžadujú samostatne, preto môže byť nová anotovaná značka zapísaná ako odkaz na objekt, ktorý sa nestiahol (#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() vyhľadáva názvy ako refs/heads/<name>, refs/tags/<name> a samotný názov, preto sa skrátená forma origin/main nenájde.

Pitmaster požaduje tenké balíky, ktoré zatiaľ nedokáže indexovať (#16). Ak server odpovie na priebežné načítanie tenkým balíkom, fetch() vyhodí výnimku PackParseException.

Už aktuálne

Ak všetky objekty, na ktoré smerujú vzdialené odkazy, už existujú lokálne, balík sa nevyžiada a fetch() sa po zistení odkazov vráti. V takom prípade sa neaktualizujú ani sledované odkazy, a to ani vtedy, keď sa vzdialené vetvy posunuli k odoslaniam, ktoré už máte (#23).

Odosielanie

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

Priebeh odosielania

  1. Čítanie vzdialeného URL: z .git/config
  2. Určenie zmien na odoslanie: vyriešte lokálnu vetvu na jej hash odovzdania
  3. Zistenie vzdialených odkazov: zistite aktuálny stav vzdialenej vetvy (na výpočet starého hashu)
  4. Kontrola posunu vpred: ak vzdialená vetva nie je predkom lokálnej vetvy, pred odoslaním čohokoľvek vyhoďte výnimku RuntimeException("Push rejected: non-fast-forward update to <branch>")
  5. Odoslanie aktualizácie odkazu: <old-hash> <new-hash> refs/heads/<branch>
  6. Odoslanie údajov balíka: balík obsahujúci objekty, ktoré vzdialený repozitár potrebuje
  7. Kontrola výsledku: server oznámi úspech alebo zlyhanie pre každý odkaz

Hook pre-push sa spustí pred odoslaním aktualizácie, pokiaľ nie sú hooky vypnuté.

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

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

Požiadavky na odoslanie

  • Aktuálna vetva musí existovať lokálne
  • Vzdialený repozitár musí byť nakonfigurovaný s URL
  • Vzdialený repozitár musí povoliť odoslanie (overenie totožnosti, oprávnenia, pravidlá proti posunu vzad)

Vynútené odoslanie

Pitmaster odošle starý hash z aktuálneho oznámenia odkazov vzdialeného repozitára a push() lokálne odmieta aktualizácie s posunom vzad. Ak chcete prepísať vzdialenú vetvu, použite vynútenie s poistkou:

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 (vnútorné fungovanie načítania)

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

Dohodnutie požadovaných a dostupných objektov vytvorí požiadavku pkt-line:

Kód
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

Server odpovie potvrdeniami a potom údajmi balíka (prípadne zabalenými do postranného pásma).

ReceivePackClient (vnútorné fungovanie odosielania)

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

Formát požiadavky na odoslanie:

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

Spracovanie chýb

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() môže tiež vyhodiť obyčajnú výnimku RuntimeException (neznámy vzdialený repozitár, objekt bez procesov) alebo PackParseException (balík, ktorý nemožno indexovať). ProtocolException a PackParseException sú podtriedami \RuntimeException, takže zachytenie \RuntimeException pokrýva všetky tieto výnimky.

Konfigurácia vzdialených repozitárov

Vzdialené repozitáre sa ukladajú do .git/config:

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

Prístup cez API konfigurácie:

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

Údaje balíka na odoslanie

V súčasnosti Pitmaster zhromažďuje objekty dosiahnuteľné z odosielaných odovzdaní, ktoré nie sú dosiahnuteľné z aktuálnych odkazov vzdialeného repozitára, a zapisuje ich do balíka ako základné objekty (bez kompresie delta). Funkčne je to správne, no výsledkom sú väčšie balíky než pri použití git push. Kompresia delta pri zápise balíkov je plánovaným vylepšením.