Ugrás a tartalomhoz

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

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

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

  1. Inicializálás: új, üres tároló létrehozása a célútvonalon
  2. Távoli tároló beállítása: a URL mentése remote.origin.url néven, majd a lekérési refspec beállítása
  3. Hivatkozások felderítése: GET /info/refs?service=git-upload-pack annak lekérdezésére, mi található a kiszolgálón
  4. Egyeztetés: want sorok küldése minden távoli hivatkozáshoz (hash szerint duplikációmentesítve)
  5. Pack fogadása: az összes kért objektumot tartalmazó pack fájl letöltése
  6. Pack írása: a pack fájl mentése ide: .git/objects/pack/
  7. Pack indexelése: az .idx fájl létrehozása a gyors kereséshez
  8. Hivatkozások beállítása: távoli követő ágak (refs/remotes/origin/*) és címkék létrehozása
  9. 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, vagy refs/heads/main értékre, ha a kiszolgáló nem küld ilyet), majd az branch.<name>.remote és branch.<name>.merge beállítása
  10. 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).

PHP
$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

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

A lekérés menete

  1. Távoli URL beolvasása: innen: .git/config (remote.<name>.url)
  2. Hivatkozások felderítése: az összes távoli hivatkozás aktuális állapotának lekérése
  3. Szükséges és meglévő objektumok egyeztetése:
    • Szükséges: azok a távoli hivatkozások, amelyekre a remote.<name>.fetch refspec 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
  4. Pack fogadása: csak az új objektumok letöltése
  5. Pack írása: mentés ide: .git/objects/pack/, az index elkészítésével együtt
  6. 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)
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');

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

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

A feltöltés menete

  1. Távoli URL beolvasása: innen: .git/config
  2. A feltöltendők meghatározása: a helyi ág feloldása a commit hashére
  3. 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)
  4. 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
  5. Hivatkozásfrissítés küldése: <old-hash> <new-hash> refs/heads/<branch>
  6. Packadatok küldése: a távoli fél által igényelt objektumokat tartalmazó pack
  7. 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.

PHP
// 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:

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 (fetch belső működése)

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

A want/have egyeztetés pkt-line kérést állít össze:

Kód
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)

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

A push-kérés formátuma:

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

Hibakezelés

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() 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:

Ini
[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:

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

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.