Siirry sisältöön

Kloonaa, nouda, lähetä

Pitmaster tukee kolmea keskeistä git-verkkotoimintoa: kloonausta (ensimmäinen lataus), noutoa (lisälataus) ja lähetystä (lataus palvelimelle). SSH URL-osoitteissa (ssh://..., user@host:path) käytetään SSH-siirtoa; muissa URL-osoitteissa käytetään älykästä HTTP-protokollaa, ja kloonaus ja nouto siirtyvät tarvittaessa yksinkertaiseen HTTP-protokollaan, jos älykäs tiedonsiirto epäonnistuu (katso Protokollat).

Kaikki kolme käynnistävät verkkosiirron, joten ne aiheuttavat virheen RuntimeException, jos kahva on avattu valitsimella ['processes' => false] tai Pitmaster::processFreeOptions().

Kloonaus

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

Uusi tietovarasto käyttää aina SHA-1. Jos kloonaus epäonnistuu eikä kohdetta ollut ennestään, osittain luotu hakemisto poistetaan.

Kloonauksen kulku

  1. Alusta: luo tyhjä tietovarasto kohdepolkuun
  2. Määritä etävarasto: tallenna URL muodossa remote.origin.url ja määritä noudon refspec
  3. Selvitä viitteet: GET /info/refs?service=git-upload-pack selvittääksesi, mitä palvelimella on
  4. Neuvottele: lähetä want-rivit kaikista etäviitteistä (hashin mukaan deduplikoituna)
  5. Vastaanota paketti: lataa pakettitiedosto, joka sisältää kaikki pyydetyt objektit
  6. Kirjoita paketti: tallenna pakettitiedosto kohteeseen .git/objects/pack/
  7. Indeksoi paketti: luo .idx-tiedosto nopeita hakuja varten
  8. Määritä viitteet: luo etähaaroja seuraavat haarat (refs/remotes/origin/*) ja tunnisteet
  9. Aseta HEAD: kohdista HEAD oletushaaraan (palvelimen symref=HEAD:...-ominaisuuden mukaan tai arvoon refs/heads/main, jos palvelin ei ilmoita sitä) ja aseta branch.<name>.remote ja branch.<name>.merge
  10. Ota käyttöön: ota haara käyttöön, jolloin työhakemisto ja indeksi kirjoitetaan

Viimeisen käyttöönoton virheet ohitetaan, joten kloonaus voi palauttaa tyhjän tai osittaisen työhakemiston (#19).

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

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

Työhakemiston luonti

Pakettitiedoston vastaanottamisen jälkeen Pitmaster kutsuu toimintoa $repo->checkout() oletushaaralle. Se kirjoittaa HEAD-commitin puun kaikki tiedostot työhakemistoon ja muodostaa indeksin puun merkinnöistä, joten status() ilmoittaa työhakemiston olevan puhdas.

Nouto

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

Noudon kulku

  1. Lue etävaraston URL: kohteesta .git/config (remote.<name>.url)
  2. Selvitä viitteet: hae kaikkien etäviitteiden nykytila
  3. Neuvottele halutuista ja olemassa olevista objekteista:
    • Halutut: etäviitteet, jotka vastaavat remote.<name>.fetch-refspec-määrityksiä (oletuksena +refs/heads/*:refs/remotes/<name>/*) ja joiden objekteja ei ole paikallisesti (kaikki, jos syvyys on määritetty)
    • Olemassa olevat: jokaisen paikallisen viitteen kärki
  4. Vastaanota paketti: lataa vain uudet objektit
  5. Kirjoita paketti: tallenna kohteeseen .git/objects/pack/ ja luo sen indeksi
  6. Päivitä seurantaviitteet: päivitä refs/remotes/<remote>/<branch> jokaista vastaavaa etähaaraa varten ja kirjoita jokainen ilmoitettu tunnisteviite. Tunnisteobjekteja ei pyydetä erikseen, joten uusi selitetty tunniste voidaan kirjoittaa viitteeksi objektiin, jota ei ladattu (#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() etsii nimiä muodoissa refs/heads/<name>, refs/tags/<name> ja nimi sellaisenaan, joten lyhyttä muotoa origin/main ei löydy.

Pitmaster pyytää ohuita paketteja, joita se ei vielä osaa indeksoida (#16). Jos palvelin vastaa lisänoutoon ohuella paketilla, fetch() aiheuttaa virheen PackParseException.

Ajantasalla

Jos kaikki etäviitteiden osoittamat objektit ovat jo paikallisesti, pakettia ei pyydetä ja fetch() palautuu viitteiden selvittämisen jälkeen. Tällöin seurantaviitteitä ei myöskään päivitetä, vaikka etähaarat olisivat siirtyneet committeihin, jotka sinulla jo on (#23).

Lähetys

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

Lähetyksen kulku

  1. Lue etävaraston URL: kohteesta .git/config
  2. Määritä lähetettävät tiedot: selvitä paikallisen haaran commit-hash
  3. Selvitä etäviitteet: hae etähaaran nykytila (vanhan hashin laskemista varten)
  4. Tarkista pikasiirto: jos etähaara ei ole paikallisen haaran edeltäjä, aiheuta virhe RuntimeException("Push rejected: non-fast-forward update to <branch>") ennen lähetystä
  5. Lähetä viitepäivitys: <old-hash> <new-hash> refs/heads/<branch>
  6. Lähetä pakettitiedot: paketti, joka sisältää etävaraston tarvitsemat objektit
  7. Tarkista tulos: palvelin ilmoittaa onnistumisesta tai epäonnistumisesta viitekohtaisesti

pre-push-koukku suoritetaan ennen päivityksen lähettämistä, ellei koukkuja ole poistettu käytöstä.

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

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

Lähetyksen vaatimukset

  • Nykyisen haaran on oltava olemassa paikallisesti
  • Etävarasto on määritettävä valitsimella URL
  • Etävaraston on hyväksyttävä lähetys (todennus, käyttöoikeudet, pikasiirron estävät käytännöt)

Pakotettu lähetys

Pitmaster lähettää etävaraston aktiivisten viitteiden ilmoituksesta saadun vanhan hashin, ja push() estää paikallisesti muut kuin pikasiirrot. Voit korvata etähaaran käyttämällä force-with-lease-toimintoa:

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 (noudon sisäinen toteutus)

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

Haluttujen ja olemassa olevien objektien neuvottelussa muodostetaan pkt-line-pyyntö:

Koodi
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

Palvelin vastaa kuittauksilla ja lähettää sitten pakettitiedot (mahdollisesti side-band-käärittyinä).

ReceivePackClient (lähetyksen sisäinen toteutus)

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

Lähetyspyynnön muoto:

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

Virheenkäsittely

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() voi aiheuttaa myös tavallisen RuntimeException-virheen (tuntematon etävarasto, kahva ei käynnistä prosesseja) tai PackParseException-virheen (pakettia ei voi indeksoida). Sekä ProtocolException että PackParseException laajentavat \RuntimeException-luokkaa, joten \RuntimeException kattaa ne kaikki.

Etävaraston määritys

Etävarastot tallennetaan kohteeseen .git/config:

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

Määritykseen pääsee API-asetuksen kautta:

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

Lähetyksen pakettitiedot

Tällä hetkellä Pitmaster kerää lähetettävien commitien kautta saavutettavat objektit, jotka eivät ole saavutettavissa etävaraston nykyisistä viitteistä, ja kirjoittaa ne pakettiin perusobjekteina (ilman delta-pakkausta). Tämä toimii oikein, mutta tuottaa suurempia paketteja kuin git push. Delta-pakkaus paketteja kirjoitettaessa on suunnitteilla oleva optimointi.