---
id: 773766eac9e6ff3c46bd686d78539aed
author: Lemon Mint
title: 'Git Monorepon uudelleenajattelu: CAS-natiivi arkkitehtuuri ihmisille ja koodausagenteille'
description: Kuinka rakentaa nolladaemon-, CAS-natiivi monorepo tavalliselle Gitille käyttäen parentless-trunk-snapshotteja, eristettyjä työtilaviitteitä, muistinvaraisia merge-puita, hierarkkisia OWNERS-tiedostoja ja O(delta)-peilisynkronointia.
language: fi
date: 2026-10-30T01:17:15.292299Z
path: /blog/posts/rethinking-the-git-monorepo-a-cas-native-architecture-for-humans-and-coding-agents-zfd3d7931
category: lowlevel
---

Jokainen kasvava insinööriorganisaatio valitsee ennemmin tai myöhemmin monen repositorion ja yhden repositorion välillä. Kun aloitetaan kymmenillä pienillä repositorioilla, elämä tuntuu aluksi yksinkertaiselta. Jokaisella kirjastolla on oma seurantajärjestelmänsä, oma CI-putkensa ja `git clone`, joka valmistuu sekunneissa. Sitten muutat tietorakennetta keskeisessä verkkopaketissa ja viettää loppuiltapäivän avaamalla versionpäivityksiä koskevia pull requesteja kuudessa alavirran repositoriossa odottaen, että CI-jonot tyhjenevät topologisessa järjestyksessä.

Siirrä kaikki perinteiseen Git-monoreporitorioon, ja vaihdat repositorioiden välisen koordinoinnin repositorion kokoon. Commit-historian kasvaessa ja binääriomaisuuden kerääntyessä aiempihin versioihin `git fetch` hidastuu. Työpuut paisuvat gigatavujen kokoisiksi. Ennen pitkää joku ehdottaa mukautetun virtuaalisen tiedostojärjestelmätaemonin, omistetun koodikatselintarkistuspalvelimen tai etärakennustyöntekijöiden laivaston pystyttämistä vain pitämään päivittäinen kehitystyö siedettävänä.

Kun autonomiset koodausagentit liittyvät tiimiin, molemmat virhetilat pahenevat. Polyrepositoriot sokeuttavat agentin projektien välisille kutsujille ja pakottavat sen jonglööraamaan tunnuksilla repositorioiden välillä. Perinteiset monorepositoriot hukuttavat agentin säiliölevyn ja verkon kaistanleveyden jokaisen tehtävän alustuksessa, kun taas samanaikaiset agentit, jotka astuvat saman haaran päälle, luovat loputtomia rebase-kilpailuja.

Inspiroituneena `google3`-arkkitehtuurista, rakensimme yhden rungon atomiset muutokset, alle sekunnissa tapahtuvan harvan käyttöönoton, hierarkkisen `OWNERS`-hallinnon ja itsenäiset avoimen lähdekoodin peilit vakiomuotoisilla Git-putkistoilla ja hyödykeobjektitallennuksella.

Suunnittelu ei tarvitse mukautettua tiedostojärjestelmätaemonia eikä kolmannen osapuolen tarkistuslaitetta, koska se käsittelee Gitiä Merkle-puuhun perustuvana sisältöosoitteellisena tallennustilana (Content-Addressable Store, CAS).

## Väärinkäsitys hitaiden Git-monorepositorioiden ytimessä

Kun kehittäjät valittavat, että Git ei skaalaudu suuriin monorepositorioihin, he törmäävät harvoin Gitin objektitietokannan rajoitukseen. Kohdassa `.git/objects` Git tallentaa kolme muuttumatonta primitiiviä, joiden avaimena on niiden SHA-1- (tai SHA-256-) sisältötiiviste:

1. **`blob`**: Yhden tiedoston raakatavutiedot.
2. **`tree`**: Hakemistoluettelo, joka yhdistää tiedostonimet ja käyttöoikeudet `blob`- tai lapsi-`tree`-tiivisteisiin.
3. **`commit`**: Pieni tekstin otsikko (tyypillisesti 200–300 tavua), joka osoittaa yhteen ainoaan juuri-`tree`-tiivisteeseen, nollaan tai useampaan vanhemman `commit`-tiivisteeseen sekä tekijän metatietoihin.

Jos monorepositorio sisältää 50 000 tiedostoa ja muokkaat yhtä riviä yhdessä tiedostossa, uusi commit käyttää uudelleen tarkat `blob`- ja `tree`-tiivisteet muille 49 999 tiedostolle.

Perinteinen Git-monorepositorio tuntuu raskaalta, koska käytäntö ylikuormittaa yhden viittauksen, `refs/heads/main`, kolmella keskenään ristiriidassa olevalla tehtävällä:

- Se pitää sisällään **jokaisen projektin koko yhtenäisen hakemistopuun**.
- Se vetää mukanaan **koko historiallisten commitien vanhempaan linkitettyä ketjua** aina ensimmäiseen päivään asti.
- Se sijaitsee osoitteessa `refs/heads/*`, nimiavaruudessa, jota Git-palvelimet mainostavat ja neuvottelevat oletusarvoisesti jokaisen `git clone`- ja `git fetch`-komennon yhteydessä.

Joka kerta kun kehittäjä tai CI-ajuri hakee haaran `refs/heads/main`, asiakas ja palvelin neuvottelevat commit-graafin saavutettavuudesta tuhansien historiallisten versioiden yli. Jos joku vahingossa commitoi 100 megatavun arkiston kolme vuotta sitten ja poisti sen seuraavana päivänä, sen `blob` roikkuu edelleen historiallisessa `tree`-rakenteessa, joka on saavutettavissa osoitteesta `refs/heads/main`.

## Viittausnimiavaruuden erottaminen

Gitin siirtoprotokolla mainostaa vain osoitteita `refs/heads/*` ja `refs/tags/*` vakion noudon aikana. Viittaukset mukautettujen nimiavaruuksien, kuten `refs/trunk/*` tai `refs/ws/*`, alla elävät aivan samassa objektivarastossa palvelimella, hyötyvät samasta pakettitiedoston deltapakkauksesta ja ne voidaan hakea tai työntää erikseen, mutta ne eivät aiheuta lainkaan kuormitusta oletuskopioille.

Näiden tehtävien jakaminen erillisiin viittauksiin yhdessä repositoriossa poistaa pullonkaulan:

```
refs/heads/main               Vain ohjaustaso (~20 kt: säännöt, .gitconfig, synkronointiasetukset, skriptit)
refs/trunk/main               Isätön tilannevedoksen commit, joka osoittaa uusimpaan yhtenäiseen juuripuuhun
refs/trunk/history            Lineaarinen isään linkitetty commit-ketju, joka jakaa tarkan juuripuun SHA-1-tiivisteen
refs/ws/<actor>/<id>          Erilliset työtilan tilannevedokset (muutosluettelot) kehittäjää tai agenttia kohden
refs/ws/cl/<change-id>/<tip>  Muuttumattomat korjaussarjat: jokainen tarkistettavaksi lähetetyn muutoksen kärki
refs/heads/land/<id>          Ohimenevä yhdistämisjonon liipaisin, joka poistetaan heti maihinnousun jälkeen
```

### 1. Ohjaustaso (`refs/heads/main`)

Oletushaara ei sisällä mitään sovelluksen lähdekoodia. Se seuraa vain repositorion toimintasääntöjä (`AGENTS.md`, `README.md`), työnkulun aliasten jaettua `.gitconfig`-tiedostoa, Starlark-synkronointiasetuksia (`sync.star`) ja palvelinpuolen yhdistämisskriptejä. Oletushaaran kloonaus siirtää noin 20 kilotavua ja valmistuu millisekunneissa.

### 2. Isätön rungon tilannevedos (`refs/trunk/main`)

Osoite `refs/trunk/main` osoittaa koko organisaation täydelliseen, yhtenäiseen lähdepuuhun (`//web/*`, `//net/*`, `//ipc/*`, `//storage/*`, `//crypto/*`, `//ai/*`, `//devtools/*`, `//corp/*`, `//third_party/*`, `//experimental/*`), mutta sen commit-objekti luodaan **ilman `-p`-isälippua**.

Koska osoitteella `refs/trunk/main` on nolla isää (`parents = []`), sen hakeminen vaatii nollan commit-graafin neuvottelun. Git-palvelin ei käy läpi historiaa; se lähettää yhden päätason commitin ja sen nykyisen hakemiston `tree`-objektit. Historialliset binäärit tai viime kuussa poistetut tiedostot ovat saavuttamattomissa kohteesta `refs/trunk/main` eikä niitä koskaan siirretä.

### 3. Lineaarinen tarkastushistoria (`refs/trunk/history`)

Kehittäjät tarvitsevat edelleen komentoja `git blame` ja `git log`, kun he tutkivat regressioita. Joka kerta kun muutos laskeutuu, yhdistämisskripti luoduntuu kaksi commit-objektia, jotka osoittavat **tarkasti samaan juurialueen `$NEW_TREE` SHA-1-tiivisteeseen**:

```bash
# Isätön tilannevedoksen commit heti syvyyden 1 työtilan tarkistusta varten
SNAP=$(git commit-tree "$NEW_TREE" -m "$RAW_MSG")

# Isään linkitetty commit lineaarista git log- ja git blame -historiaa varten
HIST=$(git commit-tree "$NEW_TREE" -p "$OLD_HIST" -m "$RAW_MSG")

git push --atomic origin \
  --force-with-lease="refs/trunk/main:${OLD_MAIN}" \
  --force-with-lease="refs/trunk/history:${OLD_HIST}" \
  "${SNAP}:refs/trunk/main" \
  "${HIST}:refs/trunk/history"
```

Sekä osoitteen `refs/trunk/main` että osoitteen `refs/trunk/history` pitäminen ei kaksinkertaista tallennustilaa. Koska molemmat commitit viittaavat identtiseen `$NEW_TREE`-tiivisteeseen, Gitin sisältöosoitteellinen tallennustila poistaa kaikkien `tree`- ja `blob`-objektien kaksoiskappaleet 100-prosenttisesti. Isättömän tilannevedoksen ylimääräinen tallennustilakustannus on yksi 250 tavun commit-otsikko.

### 4. Erilliset työtilat (`refs/ws/<actor>/<id>`)

Keskeneräiset haarat työnnetään kohteeseen `refs/ws/<actor>/<id>` eikä kohteeseen `refs/heads/*`. Sata koodausagentkia voi tallentaa tarkistuspisteitä joka minuutti saastuttamatta haara-pudotusvalikoita tai hidastamatta ihmisinsinöörien tekemää komentoa `git fetch`.

## Käyttöönotto neljässä vaiheessa: Miten harva ja blob-vapaa tarkistus toimii

Tällä viittausasettelulla uusi kehittäjä tai ohimenevä koodausagenttisäiliö ei asenna mukautettua CLI:tä tai FUSE-ajuria. Käyttöönotto vaatii neljä vaihetta vakiomuotoisella Gitillä ja repositorion aliaksilla:

```bash
# 1. Kloonaa 20 kt:n ohjaustaso blob-suodatuksella ja harvalla tilalla otettuna käyttöön
git clone --filter=blob:none --sparse https://github.com/example-org/monorepo.git
cd monorepo

# 2. Lataa repositorion aliakset ja liitä osoitteet refs/trunk/*
git config include.path ../.gitconfig
git trunk-init

# 3. Ilmoita verkkotunnuksen hakemistot, joita tähän tehtävään tarvitaan
git sparse-checkout set net/relay ipc/codec

# 4. Luo erillinen työtila, jonka juurena on uusin rungon tilannevedos
git ws-new alice/optimize-framing
```

Jokainen vaihe noutaa vain sen, mitä se tarvitsee:

1. `git clone --filter=blob:none --sparse` lataa 20 kilotavun ohjaustason osoitteessa `refs/heads/main`.
2. `git trunk-init` määrittää arvon `+refs/trunk/*:refs/remotes/origin/trunk/*` ja suorittaa blob-vapaan noudon. Git lataa `tree`-metatiedot kohteelle `refs/trunk/main` (tyypillisesti muutaman tusinan kilotavua) ja **nolla tiedostoblobia**.
3. `git sparse-checkout set net/relay ipc/codec` ohjeistaa Gitin kartiomoodin indeksisuodattimen toteuttamaan vain juurihakemiston ja kyseiset kaksi alipuuta.
4. `git ws-new` siirtyy uuteen haaraan, jonka juurena on `origin/trunk/main`. Git pyytää sen jälkeen pyynnöstä vain hakemistojen `net/relay` ja `ipc/codec` sisällä olevat `blob`-objektit. Monorepositorion loput 95 % ei koskaan ladata levylle.

### Tulevaisuuden horisontti: Osoitteen `refs/trunk/main` liittäminen virtuaaliseksi tiedostojärjestelmäksi (VFS)

Harva tarkistus blob-vapailla klooneilla kattaa päivittäiset työnkulut ilman ylimääräisiä asiakastyökaluja. Koska `refs/trunk/main` on jo valmiiksi puhdas Merkle-puu sisältöosoitteellisessa tallennustilassa, arkkitehtuuri tarjoaa myös suoran pohjan valinnaiselle käyttäjätilan virtuaaliselle tiedostojärjestelmälle (VFS) FUSE:n kautta Linuxissa, FileProviderin kautta macOS:ssä tai ProjFS:n kautta Windowsissa:

1. **Välitön koko puun liitos**: Kevyt paikallinen liitostaemon lukee osoitteen `refs/trunk/main` juuri-`tree`-objektin ja projisoi koko monorepositorion hakemistohierarkian työhakemistoon millisekunneissa, mikä poistaa komennon `git sparse-checkout set` suorittamisen tarpeen.
2. **Pyynnöstä tapahtuva blobin vikaantuminen**: Tiedostojen sisällöt pysyvät virtuaalisina, kunnes kääntäjä, IDE tai agentti antaa järjestelmäkutsun `open(2)` tai `read(2)`. Daemon noutaa sitten vastaavan SHA-1-`blob`-objektin CAS-päätepisteestä ja välimuistittaa sen paikalliselle NVMe-tallennustilalle.
3. **Copy-on-Write-peitekerros työtiloille**: Paikalliset muokkaukset kirjoitetaan kevyeen ylempään peitekerrokseen. Komennon `git ws-save` suorittaminen laskee tiivisteet vain kyseisen peitekerroksen muuttuneille poluille, kirjoittaa päivitetyt `blob`- ja `tree`-objektit ja työntää tuloksena olevan commitin kohteeseen `refs/ws/<actor>/<id>` skannaamatta muuttumattomia hakemistoja.

## Muutosluettelomalli puhtaalla Gitillä: Muistissa tapahtuva palvelinpuolen maihinnousu

Perinteisessä Git-työnkulussa ominaisuushaaran yhdistäminen joko luo kahden isän merge-commitin, mikä tekee historiasta epälineaarisen, tai vaatii kehittäjiä tekemään rebasen manuaalisesti ja kilpailemaan samanaikaisten päähaaran `main` työntöjen kanssa.

Innoitetun `google3`-muutosluettelomallin (CL) mukaisesti jokainen arkkitehtuurimme työtila edustaa **yhtä atomista muutosta** suhteessa rungon tilannevedokseen. Työskennellessään paikallisesti hakemistossa `ws/alice/optimize-framing`, kehittäjä tai agentti saattaa tehdä kymmenen inkrementaalista commitia (`wip`, `fix tests`, `lint`). Kun muutos on valmis laskeutumaan, palvelin tiivistää työtilan deltan yhdeksi lineaariseksi runkoversioksi tarkistamatta koskaan työhakemistoa CI-ajurissa.

Näin maihinnoususkripti (`land.sh`) suoritetaan muistissa käyttäen Gitin 2.40+ -putkistoa:

```bash
# 1. Tunnista juuritilannevedoksen commit, josta työtila haarautui
BASE=$(git rev-list --max-parents=0 "$WORK_SHA")

BASE_TREE=$(git rev-parse "${BASE}^{tree}")
MAIN_TREE=$(git rev-parse "${OLD_MAIN}^{tree}")
WORK_TREE=$(git rev-parse "${WORK_SHA}^{tree}")

# 2. Laske yhdistetty juuripuu muistissa ilman työpuun tarkistusta
if [ "$MAIN_TREE" = "$BASE_TREE" ]; then
  NEW_TREE="$WORK_TREE"
elif OUT=$(git merge-tree --write-tree --merge-base="$BASE_TREE" "$MAIN_TREE" "$WORK_TREE"); then
  NEW_TREE=$(head -n1 <<<"$OUT")
else
  echo "Merge conflict detected; run git ws-sync to rebase." >&2
  exit 1
fi
```

Vaiheessa 2:

- Riippumatta siitä, kuinka monta paikallista commitia työtilahaaralle tehtiin, skripti vertaa vain kolmea puutiivistettä: alkuperäistä perustilannevedosta (`$BASE_TREE`), nykyistä rungon tilannevedosta (`$MAIN_TREE`) ja lopullista työtilan puuta (`$WORK_TREE`).
- Jos osoite `refs/trunk/main` eteni kohteessa `//web/blog` Alicen muokatessa kohdetta `//net/relay`, `git merge-tree --write-tree` laskee yhdistetyn juuripuun `$NEW_TREE` suoraan Gitin objektivarastossa millisekunneissa kirjoittamatta tiedostoja ajurin tiedostojärjestelmään.
- Kun alla olevat käytäntötarkistukset läpäistään, yksi komento `git push --atomic` argumentilla `--force-with-lease` päivittää osoitteen `refs/trunk/main` ja liittää yhden lineaarisen commitin osoitteeseen `refs/trunk/history`, ja maihinnousun liipaisin `refs/heads/land/<id>` poistetaan.

## Arkkitehtuurirajojen valvonta: Nelitasoinen näkyvyys ja hierarkkiset `OWNERS`-määritykset

Koodin konsolidoiminen yhteen repositorioon toimii vain, jos toimialueiden väliset rajat valvotaan mekaanisesti ennen kuin commit voi laskeutua. Heti sen jälkeen, kun `git merge-tree --write-tree` tuottaa kohteen `$NEW_TREE` (ja ennen kuin `git push --atomic` päivittää rungon), käytäntömoottorimme suorittaa kaksi O(delta)-tarkistusta komennolle `git diff-tree "$MAIN_TREE" "$NEW_TREE"`.

### 1. Nelitasoinen verkkotunnuksen näkyvyys

Juuripuu on jaettu neljään näkyvyystasoon:

| Hakemistoetuliite | Taso | Sallitut tuonnit | Kielletyt tuonnit | `OWNERS`-portti |
| :--- | :--- | :--- | :--- | :--- |
| `//web/*`, `//net/*`, `//ipc/*`, `//storage/*`, `//crypto/*`, `//ai/*`, `//devtools/*` | Julkinen tuotanto | Julkinen tuotanto, `//third_party/*` | `//corp/*`, `//experimental/*` | Valvottu |
| `//third_party/*` | Ulkoinen toimittaja | Itsenäinen tai `//third_party/*` | Kaikki muut kuin `third_party`-verkkotunnukset | Valvottu |
| `//corp/*` | Sisäinen yksityinen | Julkinen tuotanto, `//corp/*`, `//third_party/*` | `//experimental/*` | Valvottu |
| `//experimental/<actor>/*` | Hiekkalaatikko | Mikä tahansa muu kuin `corp`-paketti | `//corp/*` | Vapautettu (automaattisesti hyväksytty) |

Jos julkinen paketti kohteessa `//web/*` yrittää tuoda sisäisen paketin kohteesta `//corp/*` tai valmistumattoman prototyypin kohteesta `//experimental/*`, maihinnousutehtävä keskeytetään ennen kuin se koskettaa osoitetta `refs/trunk/main`.

### 2. Hierarkkinen `OWNERS`-todennus ilman Gerrit-palvelinta

Tarkastaja valvoo hakemistotason `OWNERS`-tiedostoja perimyssäännöillä:

- Jokaiselle kohteiden `$MAIN_TREE` ja `$NEW_TREE` välillä muokatulle polulle tarkastaja kävelee ylöspäin tiedoston hakemistosta kohteeseen `//OWNERS`.
- Mikä tahansa `OWNERS`-tiedosto voi ilmoittaa säännön `set noparent` estääkseen perityt hyväksynnät ylemmistä hakemistoista tai säännön `per-file *.sql=@dba-team` rajatakseen hyväksynnät tiedostonimimallin mukaan.
- **Peukaloinnin estävä invariantti**: Olemassa olevat `OWNERS`-tiedostot luetaan aina kohteesta `$MAIN_TREE` (nykyinen runkopuu), ei koskaan kohteesta `$NEW_TREE`. Hyväksymätön tekijä ei voi antaa itselleen lupaa muokkaamalla `OWNERS`-tiedostoa samassa työtilan commitissa.
- **Hiekkalaatikkovapautus**: Mikä tahansa polku hiekkalaatikon `//experimental/<actor>/*` alla ohittaa `OWNERS`-tarkistukset, jotta insinöörit ja agentit voivat luoda prototyyppejä vapaasti komennolla `git ws-exp <actor> <project>` ja myöhemmin valmistua paketin tuotantoalueelle komennolla `git ws-graduate`.

## Koodikatselintarkistus ilman kitkaa: Ohimenevät PR:t ja tilannevedosdeltat

Koska osoite `refs/heads/main` pitää sisällään vain ohjaustason ja `refs/trunk/main` on isätön commit, vakio-muotoisen GitHub Pull Requestin avaaminen suoraan työtilahaarasta päähaaraan `main` yrittäisi verrata toisiinsa liittymättömiä historia-arvoja. Ratkaisimme tämän kolmella eri muutoksiin räätälöidyllä katselintarkistustyönkululla:

1. **Välittömät kahden pisteen työtiladeltat (`git ws-save` + reunadeltareititin)**
   Työtilan työntäminen komennolla `git ws-save` tuo osoitteen `refs/ws/<actor>/<id>` näkyviin kevyen reunatyöntekijän kautta. Tämä työntekijä ratkaisee osoitteen `refs/trunk/main` ja työtilaviittauksen niiden commit-SHA-tiivisteiksi ja ohjaa GitHubin kahden pisteen vertailunäkymään (`/compare/<trunk-sha>..<ws-sha>`). Pienille korjauksille tai agentin luomille muutoksille arvostelijat voivat tarkastaa deltan ja napsauttaa painiketta **Land** suoraan verkkokäyttöliittymässä.
2. **Ohimenevät GitHub Pull Requestit (`git ws-pr`)**
   Rivi riviltä tapahtuvaa katselintarkistusta varten GitHubissa komento `git ws-pr` työntää juuritilannevedoksen commitin (`$(git rev-list --max-parents=0 HEAD)`) osoitteeseen `refs/heads/base/<id>` ja työtilan kärjen osoitteeseen `refs/heads/review/<id>`. Se avaa sitten GitHub Pull Requestin, joka vertaa haaraa `review/<id>` haaraan `base/<id>`. Koska pohjahaara on se tilannevedos, josta työtila haarautui, GitHubin **Files changed** -välilehti näyttää vain työtilan deltan. Kun arvostelija napsauttaa painiketta **Merge pull request**, verkkokoukku (webhook) edistää päätason commitin osoitteeseen `refs/heads/land/<id>`, suorittaa muistissa tapahtuvan maihinnousuputken ja poistaa automaattisesti sekä osoitteen `refs/heads/base/<id>` että osoitteen `refs/heads/review/<id>`.
3. **Ulkoiset peilipull requestit**
   Avoimen lähdekoodin avustajien ei koskaan tarvitse oppia monorepositorion työnkulkua. He tekevät haarautumisen (fork) ja avaavat pull requesteja itsenäisiin peilirepositorioihin tavalliseen tapaan.

### Muutokset, korjaussarjat ja hyväksyntöjen siirto eteenpäin

Haaran nimi ei voi yksilöidä muutosta. Agentti saattaa nimetä haaransa uudelleen, korjata kärkeä tai tehdä rebasen uudemmalle rungolle useita kertoja ennen maihinnousua. Kuten Gerritissä, työnkulkutunniste tunnistaa muutoksen kärjen commit-viestissä olevasta `Change-Id: I<40 hex>` -jälkiliitteestä. Komennot `git ws-save`, `git ws-pr` ja `git ws-land` lisäävät jälkiliitteen, kun kärjessä ei ole mitään, ja säilyttävät sen muuten, joten tunnus säilyy korjausten, rebase-toimenpiteiden ja uudelleennimeämisten yli. Maihin laskeutunut rungon commit pitää jälkiliitteen ja tallentaa katselintarkistuksen pull requestin numeron, joten `git trunk-cl <number|Change-Id>` löytää sen osoitteesta `refs/trunk/history`.

Jokainen tarkistettavaksi lähetetty kärki muuttuu korjaussarjaksi. Komento `git ws-pr` työntää sen muuttumattomaan viittaukseen `refs/ws/cl/<change-id>/<tip>` katselintarkistushaaran viereen. Komennon `git ws-pr` suorittaminen uudelleen korjauksen tai `git ws-sync`-komennon jälkeen lisää korjaussarjan samaan pull requestiin. Komento `git ws-ps` listaa nykyisen muutoksen korjaussarjat, ja `git ws-psdiff <a> <b>` tulostaa kahden korjaussarjan välisen keskinäisen deltan, joten tarkastaja lukee vain sen, mikä on muuttunut edellisen kierroksen jälkeen.

GitHub sitoo hyväksynnän yhteen ainoaan commitiin, joten jokainen rebase vaatisi normaalisti uuden katselintarkistuksen. Maihinnousuputki hyväksyy myös samalle muutokselle toiselle korjaussarjalle annetun hyväksynnän, kun molemmat korjaussarjat tekevät saman korjauksen omaan pohjatilannevedokseensa. Se vertaa komentoa `git patch-id --verbatim` koko binäärideltan yli. Puhdas rebase uudemmalle rungolle laskeutuu ilman uutta katselintarkistusta, kun taas kaikki sisällön muokkaukset, mukaan lukien tyhjemerkkien tai tiedostotilan muutokset, vaativat uuden hyväksynnän.

## O(delta)-kaksisuuntainen synkronointi ja erilliset tuotantohaarat

Monet organisaatiot julkaisevat yksittäisiä paketteja itsenäisinä GitHub-repositorioina, jotta ulkoiset käyttäjät voivat käyttää komentoja `go get`, `cargo install` tai haarauttaa yksittäisen projektin koskematta monorepositorioon. Jotkut projektit, kuten staattiset blogit tai julkaisurajoitetut verkkosovellukset, ylläpitävät myös erillistä `production`-haaraa päähaaran `main` rinnalla.

Starlark-synkronointimoottorimme käsittelee sekä lähteviä vientitoimintoja että saapuvia osallistumisia **O(delta)**-ajassa, joka on verrannollinen commitissa muutettujen tiedostojen määrään eikä monorepositorion kokoon.

- **Blob-vapaa indeksin liitos**: Kun liitetään peilirepositorion alipuu monorepositorion juuripuuhun tai siitä pois, moottori asettaa arvon `GIT_NO_LAZY_FETCH=1` ja suorittaa komennon `git update-index --index-info` ja sen jälkeen komennon `git write-tree --missing-ok`. Muuttumattomat alipuut ja blob-objektit pysyvät SHA-1-tiivisteen viittaamina eikä niitä koskaan ladata CI-ajurille.
- **Vain deltoja sisältävä blob-esihaku**: Ennen vietyn commitin työntämistä itsenäiseen peiliin skripti suorittaa komennon `git diff-tree -r --diff-filter=AM "$ext_parent" "$export_tree"` ja hakee etukäteen vain muutettujen tiedostojen vanhat ja uudet blob-objektit komennolla `git cat-file --batch`. Git voi sitten laskea ohuen deltapaketin ja työntää sen peilirepositorioon sekunneissa.
- **Työnkulun ja julkaisutason haaran säilyttäminen**: Lähtevän viennin aikana synronisoija säilyttää kunkin itsenäisen repositorion oman `.github/workflows/`-hakemiston, jotta julkiset CI-ajurit suorittavat projektitestejä peilissä. Starlark-määrityksen jokainen työnkulku kohdistuu nimenomaiseen haaraan, kuten `main`, mikä säilyttää julkaisurajat repositorioille, jotka otetaan käyttöön erillisestä `production`-haarasta. Monorepositorion maihinnousut synkronoituvat peilin `main`-haaran kanssa, ja automaattiset metatiedot tai käännöksen commitit haaraan `main` synkronoituvat takaisin osoitteeseen `refs/trunk/main` verkkokoukun kautta. Päähaaran `main` ylentäminen tuotannoksi (`production`) pysyy nimenomaisena julkaisuvaiheena.

## Loppuajatuksia

Tämä arkkitehtuuri käsittelee Gitin objektivarastoa sisältöosoitteellisena tallennustilana (CAS), erottaa osoitteen `refs/heads/main` osoitteista `refs/trunk/main` ja `refs/trunk/history`, tunnistaa jokaisen muutoksen `Change-Id`-tunnuksella ja muuttumattomilla korjaussarjoilla sekä yhdistää puut muistissa komennolla `git merge-tree --write-tree`. Käyttöönoton kustannus kasvaa sen mukaan, mitä hakemistoja tehtävä tarvitsee, ja maihinnousun sekä peilisynkronoinnin kustannukset kasvavat muutoksen koon mukaan, eivät repositorion koon mukaan.
