Kloniranje, pridobivanje in pošiljanje
Pitmaster podpira tri temeljna omrežna opravila git: kloniranje (začetni prenos), pridobivanje (postopni prenos) in pošiljanje (nalaganje). URL-ji SSH (ssh://..., user@host:path) uporabljajo prenos SSH; drugi URL-ji uporabljajo pametni protokol HTTP, kloniranje in pridobivanje pa ob neuspešni pametni izmenjavi preklopita na preprosti HTTP (glejte Protokoli).
Vsi trije zaženejo omrežne prenose, zato ob uporabi povezave, odprte z ['processes' => false] ali Pitmaster::processFreeOptions(), sprožijo RuntimeException.
Kloniranje
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);
Novi repozitorij vedno uporablja SHA-1. Če kloniranje ne uspe in ciljna pot prej ni obstajala, se delno ustvarjeni imenik odstrani.
Potek kloniranja
- Inicializacija: na ciljni poti ustvarite nov prazen repozitorij
- Konfiguracija oddaljenega repozitorija: shranite URL kot
remote.origin.urlin nastavite specifikacijo referenc za pridobivanje - Odkritje referenc: izvedite
GET /info/refs?service=git-upload-pack, da ugotovite, kaj je na strežniku - Dogovarjanje: pošljite vrstice
wantza vse oddaljene reference (podvojitve odstranite glede na zgoščeno vrednost) - Prejem paketa: prenesite datoteko paketa z vsemi zahtevanimi objekti
- Zapis paketa: shranite datoteko paketa v
.git/objects/pack/ - Indeksiranje paketa: ustvarite datoteko
.idxza hitro iskanje - Nastavitev referenc: ustvarite oddaljene sledilne veje (
refs/remotes/origin/*) in oznake - Nastavitev HEAD: usmerite HEAD na privzeto vejo (glede na zmožnost
symref=HEAD:...strežnika ali narefs/heads/main, če strežnik ne pošlje nobene), nato nastavitebranch.<name>.remoteinbranch.<name>.merge - Prevzem: prevzemite to vejo; s tem se zapiše delovno drevo in indeks
Napake pri končnem prevzemu se prestrežejo in prezrejo, zato se lahko kloniranje konča s praznim ali delnim delovnim drevesom (#19).
$repo = Pitmaster::clone($url, $path);
$head = $repo->head();
echo $head->message;
$branches = $repo->branches();
$files = $repo->status();
Priprava delovnega drevesa
Po prejemu paketa Pitmaster pokliče $repo->checkout() za privzeto vejo. To zapiše vse datoteke iz drevesa objave HEAD v delovni imenik in iz vnosov v drevesu sestavi indeks, zato status() prikaže čisto delovno drevo.
Pridobivanje
// 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
Potek pridobivanja
- Branje oddaljenega URL: iz
.git/config(remote.<name>.url) - Odkritje referenc: pridobite trenutno stanje vseh oddaljenih referenc
- Dogovarjanje o zahtevanih in razpoložljivih objektih:
- Zahtevani: oddaljene reference, ki ustrezajo specifikacijam referenc
remote.<name>.fetch(privzeto+refs/heads/*:refs/remotes/<name>/*) in katerih objekti lokalno manjkajo (vse reference, če je podana globina) - Razpoložljivi: vrh vsake lokalne reference
- Zahtevani: oddaljene reference, ki ustrezajo specifikacijam referenc
- Prejem paketa: prenesite samo nove objekte
- Zapis paketa: shranite v
.git/objects/pack/in ustvarite njegov indeks - Posodobitev sledilnih referenc: posodobite
refs/remotes/<remote>/<branch>za vsako ujemajočo se oddaljeno vejo in zapišite vse oglaševane reference oznak. Objekti oznak niso zahtevani posebej, zato je lahko nova označena oznaka zapisana kot referenca na objekt, ki ni bil prenesen (#24)
// 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() išče imena pod refs/heads/<name>, refs/tags/<name> in samim imenom, zato kratke oblike origin/main ne najde.
Pitmaster zahteva tanke pakete, ki jih še ne more indeksirati (#16). Če strežnik na postopno pridobivanje odgovori s tankim paketom, fetch() sproži PackParseException.
Vse je posodobljeno
Če vsi objekti, na katere kažejo oddaljene reference, že obstajajo lokalno, se paket ne zahteva in fetch() se vrne po odkritju referenc. V tem primeru se ne posodobijo niti sledilne reference, tudi če so se oddaljene veje premaknile na objave, ki jih že imate (#23).
Pošiljanje
$repo->push(); // Push current branch to 'origin'
$repo->push('origin', 'main'); // Push a specific branch
$repo->push('upstream', 'feature'); // Push to a named remote
Potek pošiljanja
- Branje oddaljenega URL: iz
.git/config - Določitev vsebine pošiljanja: razrešite lokalno vejo v zgoščeno vrednost njene objave
- Odkritje oddaljenih referenc: pridobite trenutno stanje oddaljene veje (za izračun stare zgoščene vrednosti)
- Preverjanje pomika naprej: če oddaljena veja ni prednik lokalne, pred pošiljanjem sprožite
RuntimeException("Push rejected: non-fast-forward update to <branch>") - Pošiljanje posodobitve reference:
<old-hash> <new-hash> refs/heads/<branch> - Pošiljanje podatkov paketa: paket vsebuje objekte, ki jih potrebuje oddaljeni repozitorij
- Preverjanje rezultata: strežnik za vsako referenco sporoči uspeh ali neuspeh
Kavelj pre-push se izvede pred pošiljanjem posodobitve, razen če so kavlji onemogočeni.
// Push the current branch
$repo->push();
// Push a specific branch to a specific remote
$repo->push('origin', 'feature/login');
Zahteve za pošiljanje
- Trenutna veja mora obstajati lokalno
- Oddaljeni repozitorij mora biti nastavljen z URL
- Oddaljeni repozitorij mora sprejeti pošiljanje (preverjanje pristnosti, dovoljenja, pravilniki za nelinearni pomik naprej)
Prisilno pošiljanje
Pitmaster pošlje staro zgoščeno vrednost iz trenutnega oglaševanja referenc oddaljenega repozitorija, push() pa lokalno zavrne posodobitve z nelinearnim pomikom naprej. Če želite prepisati oddaljeno vejo, uporabite force-with-lease:
// 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 (podrobnosti pridobivanja)
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);
Dogovarjanje o zahtevanih in razpoložljivih objektih sestavi zahtevo pkt-line:
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
Strežnik odgovori s potrditvami in nato s podatki paketa (morda ovitimi v stranski pas).
ReceivePackClient (podrobnosti pošiljanja)
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);
Oblika zahteve za pošiljanje:
<old-hash> <new-hash> refs/heads/main\0<capabilities>\n
0000
PACK<pack data>
Obravnava napak
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() lahko sproži tudi navadno RuntimeException (neznan oddaljeni repozitorij, povezava brez procesov) ali PackParseException (paketa ni mogoče indeksirati). ProtocolException in PackParseException oba razširjata \RuntimeException, zato zajem \RuntimeException pokrije vse te primere.
Konfiguracija oddaljenih repozitorijev
Oddaljeni repozitoriji so shranjeni v .git/config:
[remote "origin"]
url = https://github.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
Dostop prek konfiguracije API:
$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');
Podatki paketa za pošiljanje
Trenutno Pitmaster zbere objekte, dosegljive iz objav, ki jih pošiljate, a niso dosegljivi iz trenutnih oddaljenih referenc, in jih zapiše kot osnovne objekte (brez stiskanja delt) v paket. Funkcionalno je to pravilno, vendar so paketi večji kot pri git push. Stiskanje delt pri zapisovanju paketov je načrtovana izboljšava.