Klona, hämta, skicka
Pitmaster stöder de tre grundläggande nätverksåtgärderna i git: klona (första nedladdningen), hämta (inkrementell nedladdning) och skicka (uppladdning). URL:er av typen SSH (ssh://..., user@host:path) använder transporten SSH; övriga URL:er använder smart HTTP, och kloning och hämtning faller tillbaka på dumb HTTP när den smarta kommunikationen misslyckas (se Protokoll).
Alla tre startar nätverkstransporter och ger därför RuntimeException vid användning av ett handtag som öppnats med ['processes' => false] eller Pitmaster::processFreeOptions().
Klona
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 nya repot använder alltid SHA-1. Om kloningen misslyckas och målkatalogen inte fanns sedan tidigare tas den delvis skapade katalogen bort.
Kloningsflöde
- Initiera: skapa ett nytt tomt repo på målsökvägen
- Konfigurera fjärrserver: spara URL som
remote.origin.urloch konfigurera fetch-refspecen - Upptäck referenser:
GET /info/refs?service=git-upload-packför att ta reda på vad servern har - Förhandla: skicka rader med
wantför alla fjärrreferenser (deduplicerade efter hash) - Ta emot pack: ladda ned pack-filen som innehåller alla begärda objekt
- Skriv pack: spara pack-filen i
.git/objects/pack/ - Indexera pack: generera filen
.idxför snabba uppslagningar - Konfigurera referenser: skapa fjärrspårningsgrenar (
refs/remotes/origin/*) och taggar - Ange HEAD: peka HEAD på standardgrenen (från serverns funktion
symref=HEAD:..., ellerrefs/heads/mainom servern inte skickar någon) och angebranch.<name>.remoteochbranch.<name>.merge - Checka ut: checka ut grenen, vilket skriver arbetsträdet och indexet
Fel vid den avslutande utcheckningen fångas upp och ignoreras, så en kloning kan slutföras med ett tomt eller ofullständigt arbetsträd (#19).
$repo = Pitmaster::clone($url, $path);
$head = $repo->head();
echo $head->message;
$branches = $repo->branches();
$files = $repo->status();
Skapa arbetsträdet
När pack-filen har tagits emot anropar Pitmaster $repo->checkout() för standardgrenen. Det skriver varje fil från HEAD-committens träd till arbetskatalogen och skapar indexet utifrån trädposterna, så att status() visar ett rent arbetsträd.
Hämta
// 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
Hämtningsflöde
- Läs fjärrserverns URL: från
.git/config(remote.<name>.url) - Upptäck referenser: hämta aktuellt tillstånd för alla fjärrreferenser
- Förhandla om wants/haves:
- Want: fjärrreferenser som matchas av refspecarna
remote.<name>.fetch(standardvärde+refs/heads/*:refs/remotes/<name>/*) och vars objekt saknas lokalt (alla när ett djup anges) - Have: alla lokala referenstoppar
- Want: fjärrreferenser som matchas av refspecarna
- Ta emot pack: ladda ned endast de nya objekten
- Skriv pack: spara i
.git/objects/pack/och skapa dess index - Uppdatera spårningsreferenser: uppdatera
refs/remotes/<remote>/<branch>för varje matchad fjärrgren och skriv varje annonserad taggreferens. Taggobjekt begärs inte separat, så en ny annoterad tagg kan skrivas som en referens till ett objekt som inte har laddats ned (#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() slår upp namn som refs/heads/<name>, refs/tags/<name> och själva namnet, så den korta formen origin/main hittas inte.
Pitmaster begär tunna pack-filer, som ännu inte kan indexeras (#16). Om en server svarar på en inkrementell hämtning med en tunn pack-fil gör det att fetch() ger PackParseException.
Redan uppdaterat
Om alla objekt som fjärrreferenserna pekar på redan finns lokalt begärs ingen pack-fil och fetch() returnerar efter referensupptäckten. Då uppdateras inte heller spårningsreferenserna, även om fjärrgrenarna har flyttats till commits som du redan har (#23).
Skicka
$repo->push(); // Push current branch to 'origin'
$repo->push('origin', 'main'); // Push a specific branch
$repo->push('upstream', 'feature'); // Push to a named remote
Skickningsflöde
- Läs fjärrserverns URL: från
.git/config - Avgör vad som ska skickas: slå upp den lokala grenen till dess commit-hash
- Upptäck fjärrreferenser: hämta fjärrgrenens aktuella tillstånd (för att beräkna den gamla hashen)
- Kontrollera snabbspolning: om fjärrgrenen inte är en förfader till den lokala grenen, ge
RuntimeException("Push rejected: non-fast-forward update to <branch>")innan något skickas - Skicka referensuppdatering:
<old-hash> <new-hash> refs/heads/<branch> - Skicka packdata: pack som innehåller objekten fjärrservern behöver
- Kontrollera resultatet: servern svarar med status för varje referens
Hooken pre-push körs innan uppdateringen skickas, såvida hooks inte är inaktiverade.
// Push the current branch
$repo->push();
// Push a specific branch to a specific remote
$repo->push('origin', 'feature/login');
Krav för att skicka
- Den aktuella grenen måste finnas lokalt
- En fjärrserver måste vara konfigurerad med en URL
- Fjärrservern måste godkänna skickningen (autentisering, behörigheter, regler för uppdateringar som inte är snabbspolningar)
Tvingad skickning
Pitmaster skickar den gamla hashen från fjärrserverns aktuella referensannonsering, och push() avvisar lokalt uppdateringar som inte är snabbspolningar. Om du vill skriva över en fjärrgren använder du 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 (interna detaljer för hämtning)
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);
Förhandlingen om want/have bygger en pkt-line-begäran:
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
Servern svarar med bekräftelser och därefter packdata (eventuellt med sidobandsinkapsling).
ReceivePackClient (interna detaljer för skickning)
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 för skickningsbegäran:
<old-hash> <new-hash> refs/heads/main\0<capabilities>\n
0000
PACK<pack data>
Felhantering
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 också ge ett vanligt RuntimeException (okänd fjärrserver, handtag utan processer) eller PackParseException (pack som inte kan indexeras). Både ProtocolException och PackParseException ärver från \RuntimeException, så om du fångar \RuntimeException täcks alla dessa fall.
Konfiguration av fjärrservrar
Fjärrservrar lagras i .git/config:
[remote "origin"]
url = https://github.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
Åtkomst via konfigurationens 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');
Packdata för skickning
För närvarande samlar Pitmaster in de objekt som kan nås från commitarna som skickas och som inte kan nås från fjärrserverns aktuella referenser, och skriver dem som basobjekt (utan deltakomprimering) till en pack-fil. Detta är funktionellt korrekt men skapar större pack-filer än git push. Deltakomprimering vid skrivning av pack-filer är en planerad optimering.