Klónozás, lekérés, feltöltés
Az Pitmaster a git három alapvető hálózati műveletét támogatja: a klónozást (első letöltés), a lekérést (részleges letöltés) és a feltöltést (küldés). A SSH URL-ek (ssh://..., user@host:path) a SSH átvitelt használják; más URL-ek intelligens HTTP protokollt használnak, a klónozás és a lekérés pedig buta HTTP protokollra vált, ha az intelligens adatcsere sikertelen (lásd: Protokollok).
Mindhárom hálózati átvitelt indít, ezért RuntimeException hibát dob, ha a kezelőt ['processes' => false] vagy Pitmaster::processFreeOptions() beállítással nyitották meg.
Klónozás
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);
Az új tároló mindig az SHA-1 értéket használja. Ha a klónozás nem sikerül, és a cél korábban nem létezett, a részben létrehozott könyvtár törlődik.
A klónozás menete
- Inicializálás: új, üres tároló létrehozása a célútvonalon
- Távoli tároló beállítása: a URL mentése
remote.origin.urlnéven, majd a lekérési refspec beállítása - Hivatkozások felderítése:
GET /info/refs?service=git-upload-packannak lekérdezésére, mi található a kiszolgálón - Egyeztetés:
wantsorok küldése minden távoli hivatkozáshoz (hash szerint duplikációmentesítve) - Pack fogadása: az összes kért objektumot tartalmazó pack fájl letöltése
- Pack írása: a pack fájl mentése ide:
.git/objects/pack/ - Pack indexelése: az
.idxfájl létrehozása a gyors kereséshez - Hivatkozások beállítása: távoli követő ágak (
refs/remotes/origin/*) és címkék létrehozása - HEAD beállítása: a HEAD átállítása az alapértelmezett ágra (a kiszolgáló
symref=HEAD:...képessége alapján, vagyrefs/heads/mainértékre, ha a kiszolgáló nem küld ilyet), majd azbranch.<name>.remoteésbranch.<name>.mergebeállítása - Checkout: az ág kijelentése, amely kiírja a munkafát és az indexet
A végső checkout során fellépő hibákat a rendszer elkapja és figyelmen kívül hagyja, ezért a klónozás üres vagy részleges munkafával is visszatérhet (#19).
$repo = Pitmaster::clone($url, $path);
$head = $repo->head();
echo $head->message;
$branches = $repo->branches();
$files = $repo->status();
A munkafa létrehozása
A pack fogadása után az Pitmaster meghívja az $repo->checkout() műveletet az alapértelmezett ágra. Ez a HEAD commit fájljait kiírja a munkakönyvtárba, és a fa bejegyzéseiből felépíti az indexet, így az status() tiszta munkafát jelez.
Lekérés
// 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
A lekérés menete
- Távoli URL beolvasása: innen:
.git/config(remote.<name>.url) - Hivatkozások felderítése: az összes távoli hivatkozás aktuális állapotának lekérése
- Szükséges és meglévő objektumok egyeztetése:
- Szükséges: azok a távoli hivatkozások, amelyekre a
remote.<name>.fetchrefspec illeszkedik (alapértelmezés szerint+refs/heads/*:refs/remotes/<name>/*), és amelyek objektumai hiányoznak helyben (mélység megadása esetén mindegyik) - Meglévő: az összes helyi hivatkozás végpontja
- Szükséges: azok a távoli hivatkozások, amelyekre a
- Pack fogadása: csak az új objektumok letöltése
- Pack írása: mentés ide:
.git/objects/pack/, az index elkészítésével együtt - Követő hivatkozások frissítése: minden illeszkedő távoli ág esetén az
refs/remotes/<remote>/<branch>frissítése, majd minden meghirdetett címkehivatkozás kiírása. A címkeobjektumokat a rendszer nem kéri külön, így egy új, megjegyzésekkel ellátott címke olyan objektumra mutató hivatkozásként is kiírható, amelyet nem töltött le (#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');
Az resolve() a neveket refs/heads/<name>, refs/tags/<name>, majd magát a nevet használva keresi, ezért a rövid origin/main forma nem található.
Az Pitmaster vékony packeket kér, amelyeket még nem tud indexelni (#16). Ha egy kiszolgáló vékony packkel válaszol egy részleges lekérésre, az fetch() PackParseException hibát dob.
Naprakész
Ha a távoli hivatkozások által megcélzott összes objektum már helyben is létezik, a rendszer nem kér pack fájlt, és az fetch() a hivatkozások felderítése után visszatér. Ebben az esetben a követő hivatkozások sem frissülnek, akkor sem, ha a távoli ágak már meglévő commitokra léptek tovább (#23).
Feltöltés
$repo->push(); // Push current branch to 'origin'
$repo->push('origin', 'main'); // Push a specific branch
$repo->push('upstream', 'feature'); // Push to a named remote
A feltöltés menete
- Távoli URL beolvasása: innen:
.git/config - A feltöltendők meghatározása: a helyi ág feloldása a commit hashére
- Távoli hivatkozások felderítése: a távoli ág aktuális állapotának lekérése (a régi hash kiszámításához)
- Fast-forward ellenőrzése: ha a távoli ág nem a helyi ág őse, az
RuntimeException("Push rejected: non-fast-forward update to <branch>")még bármi elküldése előtt hibát dob - Hivatkozásfrissítés küldése:
<old-hash> <new-hash> refs/heads/<branch> - Packadatok küldése: a távoli fél által igényelt objektumokat tartalmazó pack
- Eredmény ellenőrzése: a kiszolgáló hivatkozásonként jelzi a sikert vagy a hibát
Az pre-push hook az frissítés elküldése előtt fut le, kivéve, ha a hookok ki vannak kapcsolva.
// Push the current branch
$repo->push();
// Push a specific branch to a specific remote
$repo->push('origin', 'feature/login');
A feltöltés követelményei
- Az aktuális ágnak helyben léteznie kell
- Egy távoli tárolóhoz be kell állítani egy URL értéket
- A távoli tárolónak el kell fogadnia a feltöltést (hitelesítés, jogosultságok, fast-forward szabályzatok)
Kényszerített feltöltés
Pitmaster a távoli élő refhirdetésből küldi a régi hash-t, az push() pedig helyben elutasítja a nem gyorsított előrefrissítéseket. Távoli ág felülírásához használja a force-with-lease opciót:
// 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 (fetch belső működése)
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);
A want/have egyeztetés pkt-line kérést állít össze:
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
A szerver nyugtázásokkal válaszol, majd elküldi a pack-adatokat (esetleg side-band csomagolásban).
ReceivePackClient (push belső működése)
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);
A push-kérés formátuma:
<old-hash> <new-hash> refs/heads/main\0<capabilities>\n
0000
PACK<pack data>
Hibakezelés
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() egyszerű RuntimeException kivételt (ismeretlen távoli tároló, folyamat nélküli fogantyú) vagy PackParseException kivételt (nem indexelhető pack) is dobhat. Az ProtocolException és az PackParseException egyaránt az \RuntimeException osztályból származik, ezért az \RuntimeException elkapása mindegyikre kiterjed.
Távoli tárolók konfigurációja
A távoli tárolók itt vannak tárolva: .git/config:
[remote "origin"]
url = https://github.com/user/repo.git
fetch = +refs/heads/*:refs/remotes/origin/*
Elérés a konfigurációs API segítségével:
$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');
Pack-adatok push esetén
Jelenleg az Pitmaster összegyűjti a pusholt commitokból elérhető, de a távoli tároló aktuális refjeiből nem elérhető objektumokat, majd alapobjektumokként (delta-tömörítés nélkül) packba írja őket. Ez funkcionálisan helyes, de nagyobb packokat eredményez, mint az git push. A delta-tömörítés a pack írásakor tervezett optimalizálás.