Gå til indhold

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

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

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

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:

Kode
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:

Kode
<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.