GoSuda

Git Monorepo újragondolása: CAS-Native architektúra emberek és Coding Agents számára

By Lemon Mint views ...

Minden növekvő mérnöki szervezet előbb-utóbb választani kénytelen a sok tároló és az egyetlen tároló között. Kezdetben dutynyi kis tárolóval egyszerűnek tűnik az élet. Minden könyvtárnak saját hibakövető rendszere, saját CI-folyamata és másodpercek alatt lefutó git clone művelete van. Aztán megváltoztatsz egy struktúrát egy központi hálózati csomagban, és a délutánt azzal töltöd, hogy hat downstream tárolóban nyitsz verziófrissítő pull requesteket, miközben a CI-sorok topologikus sorrendben ürülnek ki.

Ha mindent egy hagyományos Git monorepóba költöztetsz, a tárolók közötti koordinációt a tárolóméretért cserélheted el. Ahogy a commit-előzmények mélyülnek, és a bináris eszközök felhalmozódnak a korábbi revíziókban, a git fetch lelassul. A munkafák gigabájtos méretűre duzzadnak. Nem telik sok időbe, mire valaki egyedi virtuális fájlrendszer-démont, dedikált kódellenőrző szervert vagy távoli build-munkások flottáját javasolja, csak hogy a napi fejlesztés elviselhető maradjon.

Amikor autonóm kódoló ügynökök csatlakoznak a csapathoz, mindkét hibamód súlyosbodik. A polirepók megvakítják az ügynököt a más projektekből érkező hívókkal szemben, és arra kényszerítik, hogy tárolók között jongláljon a hitelesítő adatokkal. A hagyományos monorepók minden feladat inicializálásakor felemésztik az ügynök konténerének lemezterületét és hálózati sávszélességét, miközben az ugyanazon ágon dolgozó párhuzamos ügynökök végtelen rebase-versenyeket okoznak.

A google3 architektúrától ihletve egyetlen törzsű (single-trunk) atomi változtatásokat, másodperc alatti ritka (sparse) onboardingot, hierarchikus OWNERS irányítást, valamint önálló nyílt forráskódú tükröket építettünk ki szabványos Git-infrastruktúrával és általános célú objektumtárolással.

A tervezés nem igényel sem egyedi fájlrendszer-démont, sem harmadik fél által szállított kódellenőrző készüléket, mert a Git-et Merkle-fa alapú tartalomcímzett tárolóként (Content-Addressable Store, CAS) kezeli.

A lassú Git monorepók szívében rejlő tévhit

Amikor a fejlesztők panaszkodnak, hogy a Git nem skálázódik nagy monorepókra, ritkán ütköznek a Git objektumadatbázisának korlátjába. A .git/objects mappában a Git három megváltoztathatatlan primitívet tárol, amelyeket SHA-1 (vagy SHA-256) tartalomhashük azonosít:

  1. blob: Egyetlen fájl nyers bájttartalma.
  2. tree: Könyvtárlista, amely fájlneveket és jogosultságokat blob vagy gyermek tree hashekhez rendel.
  3. commit: Apró szöveges fejléc (jellemzően 200 és 300 bájt közötti), amely egyetlen gyökér tree hashre, nulla vagy több szülő commit hashre és szerzői metaadatokra mutat.

Ha egy monorepo 50 000 fájlt tartalmaz, és egy fájl egyetlen sorát szerkeszted, az új commit a többi 49 999 fájl pontosan ugyanazt a blob- és tree-hashét használja újra.

A hagyományos Git monorepó azért tűnik nehéznek, mert a konvenció egyetlen hivatkozást, a refs/heads/main-t, három egymásnak ellentmondó feladattal terheli:

  • Ez tartja a teljes egyesített könyvtárfát minden projektről.
  • Magával húzza a teljes szülő-kapcsolt történeti commit-láncot egészen az első napig.
  • A refs/heads/* névtérben van, amelyet a Git-szerverek alapértelmezetten hirdetnek, és amelyről alapértelmezetten egyeztetnek minden git clone és git fetch során.

Valahányszor egy fejlesztő vagy CI-futtató lekéri a refs/heads/main-t, a kliens és a szerver több ezer történeti revízión keresztül egyeztetik a commit-gráf elérhetőségét. Ha valaki három évvel ezelőtt véletlenül commitolt egy 100 MB-os archívumot, és másnap törölte, annak blob-ja továbbra is egy történeti tree-hez kapcsolódik, amely elérhető a refs/heads/main-ről.

A hivatkozási névtér szétválasztása

A Git wire-protokollja szabványos klónozás során csak a refs/heads/* és a refs/tags/* hivatkozásokat hirdeti. Az egyedi névterekben, például a refs/trunk/* vagy a refs/ws/* alatt lévő hivatkozások ugyanabban az objektumtárban élnek a szerveren, ugyanazt a packfile delta-tömörítést használják, és kifejezetten lekérhetők vagy feltölthetők, mégsem terhelik az alapértelmezett klónozásokat.

A feladatok különböző hivatkozásokra való szétosztása egyetlen tárolón belül megszünteti a szűk keresztmetszetet:

1refs/heads/main               Csak vezérlősík (~20 KB: szabályok, .gitconfig, szinkronizációs konfiguráció, szkriptek)
2refs/trunk/main               Szülő nélküli pillanatkép-commit, amely a legfrissebb egyesített gyökér fára mutat
3refs/trunk/history            Lineáris, szülő-kapcsolt commit-lánc, amely pontosan ugyanazt a gyökér tree SHA-1 hasht használja
4refs/ws/<actor>/<id>          Izolált munkaterület-pillanatképek (változáslisták) fejlesztőnként vagy ügynökönként
5refs/ws/cl/<change-id>/<tip>  Megváltoztathatatlan patchsetek: a változtatás minden elküldött csúcsa felülvizsgálatra
6refs/heads/land/<id>          Átmeneti összefésülési sorkiváltó, amely a beolvasztás után azonnal törlődik

1. A vezérlősík (refs/heads/main)

Az alapértelmezett ág nem tartalmaz alkalmazásforrás-kódot. Csak a tároló működési szabályait (AGENTS.md, README.md), a munkafolyamat-aliasokat tartalmazó közös .gitconfig fájlt, a Starlark szinkronizációs konfigurációt (sync.star) és a szerveroldali összefésülési szkripteket követi nyomon. Az alapértelmezett ág klónozása körülbelül 20 KB-ot továbbít, és ezredmásodpercek alatt befejeződik.

2. A szülő nélküli törzspillanatkép (refs/trunk/main)

A refs/trunk/main a teljes szervezet egyesített forrásfájára mutat (//web/*, //net/*, //ipc/*, //storage/*, //crypto/*, //ai/*, //devtools/*, //corp/*, //third_party/*, //experimental/*), de commit-objektuma a -p szülőjelző nélkül készül.

Mivel a refs/trunk/main-nek nincs szülője (parents = []), lekéréséhez nincs szükség commit-gráf egyeztetésre. A Git-szerver nem járja be az előzményeket; elküldi az egyetlen fej-commitot és annak aktuális könyvtár-tree objektumait. A múlt hónapbeli történeti bináris fájlok vagy törölt fájlok nem érhetők el a refs/trunk/main-ről, és soha nem kerülnek átvitelre.

3. A lineáris auditelőzmény (refs/trunk/history)

A fejlesztőknek továbbra is szükségük van a git blame és a git log parancsokra regressziók vizsgálatakor. Minden alkalommal, amikor egy változtatás beolvad, az összefésülési szkript két commit-objektumot hoz létre, amelyek pontosan ugyanarra a $NEW_TREE gyökér-SHA-1 hashre mutatnak:

 1# Szülő nélküli pillanatkép-commit azonnali, mélység-1 munkaterület-lekérésekhez
 2SNAP=$(git commit-tree "$NEW_TREE" -m "$RAW_MSG")
 3
 4# Szülő-kapcsolt commit a lineáris git log és git blame parancsokhoz
 5HIST=$(git commit-tree "$NEW_TREE" -p "$OLD_HIST" -m "$RAW_MSG")
 6
 7git push --atomic origin \
 8  --force-with-lease="refs/trunk/main:${OLD_MAIN}" \
 9  --force-with-lease="refs/trunk/history:${OLD_HIST}" \
10  "${SNAP}:refs/trunk/main" \
11  "${HIST}:refs/trunk/history"

A refs/trunk/main és a refs/trunk/history megtartása nem duplázza meg a tárhelyigényt. Mivel mindkét commit ugyanarra a $NEW_TREE hashre hivatkozik, a Git tartalomcímzett tárolója 100%-ban deduplikálja a tree és blob objektumokat. A szülő nélküli pillanatkép extra tárhelyköltsége mindössze egy 250 bájtos commit-fejléc.

4. Izolált munkaterületek (refs/ws/<actor>/<id>)

A folyamatban lévő munkát tartalmazó ágak a refs/heads/* helyett a refs/ws/<actor>/<id> alá kerülnek. Száz kódoló ügynök percenként menthet ellenőrzőpontokat anélkül, hogy beszennyezné az ágválasztó listákat, vagy lassítaná a git fetchműveletet az emberi mérnökök számára.

Onboarding négy lépésben: a ritka és blob nélküli kijelentkezés működése

Ezzel a hivatkozási elrendezéssel egy új fejlesztőnek vagy egy átmeneti kódoló ügynök konténerének nem kell egyedi CLI-t vagy FUSE-illesztőprogramot telepítenie. Az onboarding négy lépésből áll, szabványos Git-tel és a tároló aliasaival:

 1# 1. Klónozás a 20 KB-os vezérlősíkról blob-szűréssel és ritka mód engedélyezésével
 2git clone --filter=blob:none --sparse https://github.com/example-org/monorepo.git
 3cd monorepo
 4
 5# 2. Tárolóaliasok betöltése és a refs/trunk/* csatolása
 6git config include.path ../.gitconfig
 7git trunk-init
 8
 9# 3. A feladathoz szükséges domain-könyvtárak megadása
10git sparse-checkout set net/relay ipc/codec
11
12# 4. Izolált munkaterület létrehozása a legfrissebb törzspillanatkép alapján
13git ws-new alice/optimize-framing

Minden lépés csak azt tölti le, amire szüksége van:

  1. A git clone --filter=blob:none --sparse letölti a 20 KB-os vezérlősíkot a refs/heads/main ágról.
  2. A git trunk-init beállítja a +refs/trunk/*:refs/remotes/origin/trunk/* leképezést, és blob nélküli lekérést futtat. A Git letölti a refs/trunk/main tree metaadatait (jellemzően néhány tucat kilobájt), és nulla fájl-blobot.
  3. A git sparse-checkout set net/relay ipc/codec utasítja a Git kúpos módú index-szűrőjét, hogy csak a gyökérkönyvtárt és ez a két alfát jelenítse meg.
  4. A git ws-new új ágra vált, amely az origin/trunk/main alapján jön létre. A Git ezután csak igény szerint kéri le a net/relay és az ipc/codec mappákban lévő blob objektumokat. A monorepo fennmaradó 95%-a soha nem kerül a lemezre.

Jövőbeli kilátás: a refs/trunk/main csatolása virtuális fájlrendszerként (VFS)

A ritka kijelentkezés blob nélküli klónokkal extra kliens-eszközök nélkül lefedi a napi munkafolyamatokat. Mivel a refs/trunk/main már tiszta Merkle-fa egy tartalomcímzett tárolóban, az architektúra közvetlen alapot nyújt egy opcionális felhasználói térbeli virtuális fájlrendszerhez (VFS), amely Linuxon FUSE-n, macOS-en FileProvideren, Windowson pedig ProjFS-en keresztül valósulhat meg:

  1. Azonnali teljes fa csatolás: Egy könnyű helyi csatolási démon beolvassa a refs/trunk/main gyökér tree objektumát, és ezredmásodpercek alatt levetíti a teljes monorepo könyvtárhierarchiát a munkakönyvtárba, így nincs szükség a git sparse-checkout set futtatására.
  2. Igény szerinti blob-betöltés: A fájltartalmak virtuálisak maradnak mindaddig, amíg egy fordító, IDE vagy ügynök nem ad ki open(2) vagy read(2) rendszerhívást. A démon ekkor lekéri a megfelelő SHA-1 blob-ot a CAS végpontról, és helyben, NVMe tárolón gyorsítótárazza.
  3. Copy-on-Write átfedés a munkaterületekhez: A helyi szerkesztések egy könnyű felső átfedési rétegbe íródnak. A git ws-save futtatása csak az átfedés módosított útvonalait hashelei, kiírja a frissített blob és tree objektumokat, és a keletkező commitot feltölti a refs/ws/<actor>/<id> alá, anélkül hogy a változatlan könyvtárakat végigpásztázná.

A változáslista-modell tiszta Git-en: memóriabeli landolás szerveroldalon

Egy hagyományos Git-munkafolyamatban egy funkcióág beolvasztása vagy két szülős összefésülési commitot hoz létre, ami nem lineárissá teszi az előzményeket, vagy kézi rebase-et igényel, és versenyt kell futtatni a main ágra érkező párhuzamos feltöltésekkel.

A google3 Changelist (CL) modelltől ihletve architektúránkban minden munkaterület egyetlen atomi változást jelent a törzspillanatképhez képest. Amíg a fejlesztő vagy ügynök a ws/alice/optimize-framing munkaterületen dolgozik, tíz inkrementális commitot készíthet (wip, fix tests, lint). Amikor a változtatás készen áll a beolvasztásra, a szerver a munkaterület-deltát egyetlen lineáris törzsrevízióba tömöríti anélkül, hogy a CI-futtatón bármilyen munkakönyvtárat kijelentkeztetne.

Így fut a landoló szkript (land.sh) memóriában a Git 2.40+ alacsony szintű parancsaival:

 1# 1. Annak a gyökér-pillanatkép commitnak az azonosítása, ahonnan a munkaterület elágazott
 2BASE=$(git rev-list --max-parents=0 "$WORK_SHA")
 3
 4BASE_TREE=$(git rev-parse "${BASE}^{tree}")
 5MAIN_TREE=$(git rev-parse "${OLD_MAIN}^{tree}")
 6WORK_TREE=$(git rev-parse "${WORK_SHA}^{tree}")
 7
 8# 2. Az összefésült gyökérfa kiszámítása memóriában, munkafa-kijelentkezés nélkül
 9if [ "$MAIN_TREE" = "$BASE_TREE" ]; then
10  NEW_TREE="$WORK_TREE"
11elif OUT=$(git merge-tree --write-tree --merge-base="$BASE_TREE" "$MAIN_TREE" "$WORK_TREE"); then
12  NEW_TREE=$(head -n1 <<<"$OUT")
13else
14  echo "Merge conflict detected; run git ws-sync to rebase." >&2
15  exit 1
16fi

A 2. lépésben:

  • Függetlenül attól, hány helyi commit készült a munkaterület ágán, a szkript csak három tree hasht hasonlít össze: az eredeti alappillanatképet ($BASE_TREE), az aktuális törzspillanatképet ($MAIN_TREE) és a végső munkaterület-fát ($WORK_TREE).
  • Ha a refs/trunk/main előrelépett a //web/blog alatt, miközben Alice a //net/relay részt szerkesztette, a git merge-tree --write-tree ezredmásodpercek alatt kiszámítja a kombinált gyökérfát, a $NEW_TREE-t, közvetlenül a Git objektumtárában, anélkül hogy fájlokat írna a futtató fájlrendszerére.
  • Miután az alábbi szabályzati ellenőrzések átmennek, egyetlen git push --atomic --force-with-lease kapcsolóval frissíti a refs/trunk/main-t, egy lineáris commitot fűz a refs/trunk/history-hoz, és a refs/heads/land/<id> landoló kiváltó törlődik.

Az architektúra-határok kikényszerítése: négyszintű láthatóság és hierarchikus OWNERS

A kód egyetlen tárolóba konszolidálása csak akkor működik, ha a domainek közötti határokat gépiesen kikényszerítik, mielőtt egy commit beolvadhatna. Közvetlenül azután, hogy a git merge-tree --write-tree előállítja a $NEW_TREE-t (és mielőtt a git push --atomic frissítené a törzset), szabályzatmotorunk két O(delta) ellenőrzést futtat a git diff-tree "$MAIN_TREE" "$NEW_TREE" kimenetén.

1. Négyszintű domain-láthatóság

A gyökérfát négy láthatósági szintre osztjuk:

Könyvtár-előtagSzintEngedélyezett importokTiltott importokOWNERS kapu
//web/*, //net/*, //ipc/*, //storage/*, //crypto/*, //ai/*, //devtools/*Nyilvános élesNyilvános éles, //third_party/*//corp/*, //experimental/*Kényszerített
//third_party/*Külső szállítóÖnálló vagy //third_party/*Minden nem third_party domainKényszerített
//corp/*Belső, zártNyilvános éles, //corp/*, //third_party/*//experimental/*Kényszerített
//experimental/<actor>/*HomokozóBármely nem corp csomag//corp/*Mentesített (automatikusan jóváhagyott)

Ha a //web/* egyik nyilvános csomagja egy //corp/* belső csomagot vagy a //experimental/* alatti, még át nem vett prototípust próbál importálni, a landoló feladat megszakad, mielőtt hozzányúlna a refs/trunk/main-hez.

2. Hierarchikus OWNERS ellenőrzés Gerrit-szerver nélkül

Az ellenőrző a könyvtárszintű OWNERS fájlokat öröklési szabályokkal kényszeríti ki:

  • A $MAIN_TREE és a $NEW_TREE között módosított minden útvonalnál az ellenőrző a fájl könyvtárától felfelé halad a //OWNERS fájlig.
  • Bármely OWNERS fájl deklarálhatja a set noparent utasítást, amely megakadályozza a szülőkönyvtárakból öröklött jóváhagyásokat, vagy a per-file *.sql=@dba-team utasítást, amely a jóváhagyásokat fájlnév-minta szerint szűkíti.
  • Manipulációvédelmi invariáns: A meglévő OWNERS fájlokat mindig a $MAIN_TREE-ből (az aktuális törzsfából) olvassuk, soha nem a $NEW_TREE-ből. Egy jogosulatlan szerző nem adhat magának engedélyt úgy, hogy ugyanabban a munkaterület-commitban módosítja az OWNERS fájlt.
  • Homokozó-mentesség: A //experimental/<actor>/* alatti útvonalak megkerülik az OWNERS ellenőrzéseket, így a mérnökök és ügynökök szabadon prototipizálhatnak a git ws-exp <actor> <project> paranccsal, később pedig a git ws-graduate paranccsal átemelhetik a csomagot éles domainbe.

Kódellenőrzés súrlódás nélkül: átmeneti PR-ek és pillanatkép-diffek

Mivel a refs/heads/main csak a vezérlősíkot tartalmazza, a refs/trunk/main pedig szülő nélküli commit, egy szabványos GitHub Pull Request megnyitása közvetlenül a munkaterület ágából a main ágba összeálíthatatlan történetek összehasonlítását kísérelné meg. Ezt három, a változások típusához igazított felülvizsgálati munkafolyamattal oldottuk meg:

  1. Azonnali kétpontos munkaterület-diffek (git ws-save + élszintű diff-útválasztó) A git ws-save paranccsal feltöltött munkaterület a refs/ws/<actor>/<id> hivatkozást egy könnyű élszintű munkavégző felé teszi elérhetővé. Ez a munkavégző a refs/trunk/main-t és a munkaterület-hivatkozást commit SHA-kra oldja fel, majd átirányít a GitHub kétpontos összehasonlító nézetére (/compare/<trunk-sha>..<ws-sha>). Kisméretű foltok vagy ügynökök által generált változtatások esetén a felülvizsgálók megvizsgálhatják a diffet, és közvetlenül a webes felületen a Land gombra kattinthatnak.
  2. Átmeneti GitHub Pull Requestek (git ws-pr) A soronkénti GitHub-os felülvizsgálathoz a git ws-pr feltölti a gyökér-pillanatkép commitot ($(git rev-list --max-parents=0 HEAD)) a refs/heads/base/<id> alá, a munkaterület csúcsát pedig a refs/heads/review/<id> alá. Ezután GitHub Pull Requestet nyit, amely a review/<id>-t a base/<id>-hez hasonlítja. Mivel az alapág az a pillanatkép, amelyből a munkaterület elágazott, a GitHub Files changed fülén csak a munkaterület-delta látszik. Amikor a felülvizsgáló a Merge pull request gombra kattint, egy webhook előlépteti a fej-commitot a refs/heads/land/<id> alá, lefuttatja a memóriabeli landoló folyamatot, és automatikusan törli mind a refs/heads/base/<id>, mind a refs/heads/review/<id> hivatkozást.
  3. Külső tükör-pull requestek A nyílt forráskódú közreműködőknek soha nem kell megtanulniuk a monorepo munkafolyamatát. Szokásos módon forkolnak, és pull requesteket nyitnak az önálló tükör-tárolókhoz.

Változások, patchsetek és jóváhagyások átvitele

Egy ágnév nem azonosíthat egy változtatást. Egy ügynök átnevezheti az ágát, módosíthatja a csúcsot, vagy többször rebase-elhet egy újabb törzsre a beolvasztás előtt. A Gerrithez hasonlóan a munkafolyamat a változtatást a csúcs commit üzenetében található Change-Id: I<40 hex> záradékkal azonosítja. A git ws-save, a git ws-pr és a git ws-land hozzáadja a záradékot, ha a csúcsban nincs, egyébként megtartja, így az azonosító túléli a módosításokat, rebase-eket és átnevezéseket. A beolvasztott törzs-commit megtartja a záradékot, és rögzíti a hozzá tartozó felülvizsgálati pull request számát, így a git trunk-cl <number|Change-Id> megtalálja a refs/trunk/history-ban.

Minden felülvizsgálatra küldött csúcs patchsetté válik. A git ws-pr a megváltoztathatatlan refs/ws/cl/<change-id>/<tip> hivatkozásra tölti fel a felülvizsgálati ágak mellett. Ha a git ws-pr futtatása módosítás vagy git ws-sync után ismétlődik, a patchset ugyanahhoz a pull requesthez adódik hozzá. A git ws-ps listázza az aktuális változás patchsetjeit, a git ws-psdiff <a> <b> pedig kiírja a két patchset közötti interdiffet, így a felülvizsgáló csak az utolsó kör óta változott részeket olvassa el.

A GitHub a jóváhagyást egyetlen commithoz köti, ezért minden rebase általában új felülvizsgálatot igényelne. A landoló folyamat elfogad olyan jóváhagyást is, amelyet ugyanazon változás egy másik patchsetjére adtak, ha mindkét patchset ugyanazt a foltot alkalmazza a saját alappillanatképére. Ehhez a git patch-id --verbatim parancsot hasonlítja össze a teljes bináris diffen. Egy tiszta rebase egy újabb törzsre újabb felülvizsgálat nélkül beolvad, míg bármilyen tartalmi szerkesztés, beleértve a szóköz- vagy fájlmódváltozást is, új jóváhagyást igényel.

O(delta) kétirányú szinkronizáció és külön éles ágak

Sok szervezet az egyes csomagokat önálló GitHub-tárolóként teszi közzé, hogy a külső felhasználók a monorepo érintése nélkül go get-et, cargo install-t vagy egyetlen projekt forkolását végezhessék el. Egyes projektek, például a statikus blogok vagy a kiadáshoz kötött webalkalmazások, a main mellett külön production ágat is fenntartanak.

Starlark szinkronizációs motorunk mind a kimenő exportokat, mind a bejövő hozzájárulásokat O(delta) idő alatt kezeli, amely a commitban módosult fájlok számával arányos, nem pedig a monorepo méretével:

  • Blob nélküli index-összefűzés: Amikor egy tükör-tároló részfáját a monorepo gyökérfájába illesztjük be, vagy onnan kiemeljük, a motor beállítja a GIT_NO_LAZY_FETCH=1 változót, majd lefuttatja a git update-index --index-info parancsot, utána pedig a git write-tree --missing-ok parancsot. A nem módosított részfák és blobok SHA-1 hivatkozással maradnak, és soha nem töltődnek le a CI-futtatóra.
  • Csak a delta blobjainak előzetes lekérése: Mielőtt egy exportált commitot egy önálló tükörre feltöltenénk, a szkript lefuttatja a git diff-tree -r --diff-filter=AM "$ext_parent" "$export_tree" parancsot, és a git cat-file --batch segítségével csak a módosított fájlok régi és új blobjait tölti le előre. A Git ezután vékony deltacsomagot tud számolni, és másodpercek alatt feltölteni a tükör-tárolóba.
  • Munkafolyamat- és kiadási ágak megőrzése: Kimenő export során a szinkronizáló minden önálló tároló saját .github/workflows/ könyvtárát megőrzi, így a nyilvános CI-futtatók a tükrön futtatják a projekt teszteit. A Starlark konfigurációban minden munkafolyamat egy explicit ágat céloz meg, például a main-t, ami megőrzi a kiadási kapukat azoknál a tárolóknál, amelyek külön production ágról telepítenek. A monorepo beolvasztásai a tükör main ágával szinkronizálnak, a main ágon automatikusan létrejövő metaadat- vagy fordítási commitok pedig webhookon keresztül visszaszinkronizálódnak a refs/trunk/main-be. A main production ágra léptetése továbbra is explicit kiadási lépés marad.

Záró gondolatok

Ez az architektúra a Git objektumtárát tartalomcímzett tárolóként kezeli, szétválasztja a refs/heads/main-t a refs/trunk/main-től és a refs/trunk/history-tól, minden változtatást Change-Id azonosítóval és megváltoztathatatlan patchsetekkel különít el, és a fákat memóriában egyesíti a git merge-tree --write-tree paranccsal. Az onboarding költsége azzal a könyvtármérettel nő, amelyre egy feladatnak szüksége van, a landolás és a tükör-szinkronizáció költsége pedig a változtatás méretével, nem pedig a tároló méretével.