GoSuda

İnsanlar ve Kodlama Ajanları İçin CAS Temelli Bir Mimari: Git Monorepo'sunu Yeniden Düşünmek

By Lemon Mint views ...

Büyüyen her mühendislik organizasyonu er ya da geç çok sayıda depo ile tek depo arasında bir seçim yapar. Küçük depoların onlarcasıyla başlayınca hayat ilk başta basit görünür. Her kütüphanenin kendi issue tracker'ı, kendi CI pipeline'ı ve saniyeler içinde tamamlanan bir git clone işlemi vardır. Ardından çekirdek bir ağ paketindeki bir struct'ı değiştirirsiniz ve günün geri kalanını altı downstream depoda sürüm güncelleme pull request'leri açarak, CI kuyruklarının topolojik sırayla boşalmasını bekleyerek geçirirsiniz.

Her şeyi geleneksel bir Git monorepo'suna taşırsanız, depolar arası koordinasyonu depo boyutuyla takas etmiş olursunuz. Commit geçmişi derinleştikçe ve geçmiş revizyonlarda ikili varlıklar birikdikçe git fetch yavaşlar. Çalışma ağaçları gigabaytlara şişer. Kısa süre sonra, yalnızca günlük geliştirmeyi katlanılabilir tutmak için özel bir sanal dosya sistemi daemon'ı, özel bir kod inceleme sunucusu ya da bir uzak build worker filosu kurulması önerilir.

Otonom kodlama ajanları ekibe katıldığında, her iki başarısızlık modu da kötüleşir. Polyrepo'lar bir ajanı projeler arası çağıranlara karşı kör bırakır ve onu depolar arasında kimlik bilgilerini yönetmeye zorlar. Geleneksel monorepo'lar ise her görev başlatmada ajanın konteyner diskini ve ağ bant genişliğini tüketir; aynı dalda çalışan eşzamanlı ajanlar ise bitmek bilmeyen rebase yarışları yaratır.

google3 mimarisinden ilham alarak tek gövdeli (single-trunk) atomik değişiklikler, saniyenin altında seyrek (sparse) onboarding, hiyerarşik OWNERS yönetişimi ve standart Git altyapısı ile sıradan nesne depolaması kullanan bağımsız açık kaynak aynaları geliştirdik.

Tasarım ne özel bir dosya sistemi daemon'ına ne de üçüncü taraf bir inceleme cihazına ihtiyaç duyar; çünkü Git'i bir Merkle-tree Content-Addressable Store (CAS) olarak ele alır.

Yavaş Git Monorepo'larının Kalbindeki Yanılgı

Geliştiriciler Git'in büyük monorepo'lara ölçeklenmediğinden yakındığında, nadiren Git'in nesne veritabanındaki bir sınıra çarpmaktadırlar. .git/objects içinde Git, SHA-1 (veya SHA-256) içerik karmasıyla anahtarlanmış üç değişmez ilkel veri saklar:

  1. blob: Tek bir dosyanın ham bayt içeriği.
  2. tree: Dosya adlarını ve izinlerini blob ya da alt tree karmalarına eşleyen bir dizin listesi.
  3. commit: Tek bir kök tree karmasına, sıfır veya daha fazla üst commit karmasına ve yazar meta verisine işaret eden küçük bir metin başlığı (genellikle 200 ila 300 bayt).

Bir monorepo 50.000 dosya içeriyorsa ve bir dosyada tek bir satırı düzenlerseniz, yeni commit diğer 49.999 dosyanın tam blob ve tree karmalarını yeniden kullanır.

Geleneksel bir Git monorepo'su ağır hissettirir, çünkü gelenek tek bir referansa, refs/heads/main'e, birbiriyle çelişen üç işi yükler:

  • Her projenin tüm birleşik dizin ağacını tutar.
  • İlk güne kadar uzanan tüm üst-bağlantılı geçmiş commit zincirini sürükler.
  • Git sunucularının her git clone ve git fetch sırasında varsayılan olarak duyurduğu ve müzakere ettiği ad alanı olan refs/heads/* içinde yer alır.

Bir geliştirici ya da CI runner'ı her refs/heads/main getirdiğinde, istemci ve sunucu binlerce tarihsel revizyon boyunca commit grafiği erişilebilirliğini müzakere eder. Biri üç yıl önce yanlışlıkla 100 MB'lık bir arşiv commit edip ertesi gün silmişse, onun blob'u hâlâ refs/heads/main'den erişilebilir bir tarihsel tree'ye bağlıdır.

Referans Ad Alanını Ayırmak

Git'in wire protokolü, standart bir clone sırasında yalnızca refs/heads/* ve refs/tags/* referanslarını duyurur. refs/trunk/* ya da refs/ws/* gibi özel ad alanları altındaki referanslar sunucuda tam olarak aynı nesne deposunda yaşar, aynı packfile delta sıkıştırmasından yararlanır ve açıkça getirilebilir ya da itilebilir; yine de varsayılan clone'lara sıfır ek yük getirmez.

Tek bir depodaki bu işleri ayrı referanslara bölmek darboğazı ortadan kaldırır:

1refs/heads/main               Yalnızca kontrol düzlemi (~20 KB: kurallar, .gitconfig, senkronizasyon yapılandırması, betikler)
2refs/trunk/main               En son birleşik kök tree'ye işaret eden, üst öğesi olmayan snapshot commit
3refs/trunk/history            Tam olarak aynı kök tree SHA-1'ini paylaşan doğrusal üst-bağlantılı commit zinciri
4refs/ws/<actor>/<id>          Geliştirici veya ajan başına izole çalışma alanı snapshot'ları (changelist'ler)
5refs/ws/cl/<change-id>/<tip>  Değişmez patchset'ler: inceleme için gönderilen bir değişikliğin her ucu
6refs/heads/land/<id>          Ana dala alınır alınmaz silinen geçici merge-queue tetikleyicisi

1. Kontrol Düzlemi (refs/heads/main)

Varsayılan dal hiçbir uygulama kaynak kodu içermez. Yalnızca depo işletim kurallarını (AGENTS.md, README.md), iş akışı takma adlarından oluşan ortak bir .gitconfig'i, Starlark senkronizasyon yapılandırmasını (sync.star) ve sunucu tarafı birleştirme betiklerini izler. Varsayılan dalı clone'lamak yaklaşık 20 KB aktarır ve milisaniyeler içinde tamamlanır.

2. Üst Öğesiz Trunk Snapshot'ı (refs/trunk/main)

refs/trunk/main, tüm organizasyonun (//web/*, //net/*, //ipc/*, //storage/*, //crypto/*, //ai/*, //devtools/*, //corp/*, //third_party/*, //experimental/*) eksiksiz, birleşik kaynak ağacına işaret eder; ancak commit nesnesi -p üst bayrağı olmadan oluşturulur.

refs/trunk/main'in sıfır üst öğesi olduğundan (parents = []), onu getirmek sıfır commit grafiği müzakeresi gerektirir. Git sunucusu geçmişi taramaz; tek başlık commit'i ve geçerli dizin tree nesnelerini gönderir. Geçen aya ait tarihsel ikili dosyalar ya da silinmiş dosyalar refs/trunk/main'den erişilemez ve hiçbir zaman aktarılmaz.

3. Doğrusal Denetim Geçmişi (refs/trunk/history)

Geliştiricilerin regresyonları incelerken yine de git blame ve git log komutlarına ihtiyacı vardır. Her değişiklik ana dala alındığında birleştirme betiği, tam olarak aynı kök $NEW_TREE SHA-1 karmasına işaret eden iki commit nesnesi oluşturur:

 1# Anlık derinlik-1 çalışma alanı checkout'ları için üst öğesiz snapshot commit
 2SNAP=$(git commit-tree "$NEW_TREE" -m "$RAW_MSG")
 3
 4# Doğrusal git log ve git blame için üst-bağlantılı commit
 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"

Hem refs/trunk/main hem de refs/trunk/history tutmak depolama ayak izini ikiye katlamaz. Her iki commit de aynı $NEW_TREE karmasına başvurduğundan, Git'in içerik adresli deposu tree ve blob nesnelerinin %100'ünü tekilleştirir. Üst öğesiz snapshot'ın ek depolama maliyeti yalnızca 250 baytlık bir commit başlığıdır.

4. İzole Çalışma Alanları (refs/ws/<actor>/<id>)

Üzerinde çalışılan dallar refs/heads/* yerine refs/ws/<actor>/<id> altına itilir. Yüzlerce kodlama ajanı her dakika kontrol noktası kaydedebilir; dal açılır listelerini kirletmeden ya da insan mühendisler için git fetch işlemini yavaşlatmadan.

Dört Adımda Onboarding: Seyrek + Blobsuz Checkout Nasıl Çalışır

Bu referans düzeniyle yeni bir geliştirici ya da geçici bir kodlama ajanı konteyneri özel bir CLI ya da FUSE sürücüsü kurmaz. Onboarding, standart Git ve deponun takma adlarıyla dört adımda tamamlanır:

 1# 1. Blob filtreleme ve seyrek mod etkinleştirilmiş şekilde 20 KB'lık kontrol düzlemini clone'layın
 2git clone --filter=blob:none --sparse https://github.com/example-org/monorepo.git
 3cd monorepo
 4
 5# 2. Depo takma adlarını yükleyin ve refs/trunk/* referanslarını ekleyin
 6git config include.path ../.gitconfig
 7git trunk-init
 8
 9# 3. Bu görev için gereken alan dizinlerini bildirin
10git sparse-checkout set net/relay ipc/codec
11
12# 4. En son trunk snapshot'ına köklenmiş izole bir çalışma alanı oluşturun
13git ws-new alice/optimize-framing

Her adım yalnızca ihtiyaç duyduğunu getirir:

  1. git clone --filter=blob:none --sparse, refs/heads/main üzerindeki 20 KB'lık kontrol düzlemini indirir.
  2. git trunk-init, +refs/trunk/*:refs/remotes/origin/trunk/* yapılandırır ve blobsuz bir fetch çalıştırır. Git, refs/trunk/main için tree meta verisini (genellikle birkaç on kilobayt) indirir ve sıfır dosya blob'u indirir.
  3. git sparse-checkout set net/relay ipc/codec, Git'in cone-mode indeks filtresine yalnızca kök dizini ve bu iki alt ağacı somutlaştırmasını söyler.
  4. git ws-new, origin/trunk/main'e köklenmiş yeni bir dala geçer. Git bundan sonra yalnızca net/relay ve ipc/codec içindeki blob nesnelerini talep üzerine ister. Monorepo'nun kalan %95'i asla diske indirilmez.

Gelecek Ufku: refs/trunk/main'i Sanal Dosya Sistemi (VFS) Olarak Bağlamak

Blobsuz clone'larla seyrek checkout, ek istemci araçları olmadan günlük iş akışlarını karşılar. refs/trunk/main zaten Content-Addressable Store içinde saf bir Merkle ağacı olduğundan, mimari ayrıca Linux'ta FUSE, macOS'ta FileProvider ya da Windows'ta ProjFS aracılığıyla isteğe bağlı bir kullanıcı alanı Sanal Dosya Sistemi (VFS) için doğrudan bir temel sağlar:

  1. Anlık Tam Ağaç Bağlama: Hafif bir yerel bağlama daemon'ı refs/trunk/main'in kök tree nesnesini okur ve tüm monorepo dizin hiyerarşisini milisaniyeler içinde çalışma dizinine yansıtır; git sparse-checkout set çalıştırma gereksinimini ortadan kaldırır.
  2. Talep Üzerine Blob Yükleme (On-Demand Blob Faulting): Dosya içerikleri, bir derleyici, IDE ya da ajan bir open(2) ya da read(2) sistem çağrısı yapana kadar sanal kalır. Daemon bunun üzerine ilgili SHA-1 blob'unu CAS uç noktasından getirir ve yerel NVMe depolamada önbelleğe alır.
  3. Çalışma Alanları için Kopyala-Yaz Katmanı (Copy-on-Write Overlay): Yerel düzenlemeler hafif bir üst overlay katmanına yazılır. git ws-save çalıştırmak yalnızca bu overlay'deki kirli yolların karmasını alır, güncellenmiş blob ve tree nesnelerini yazar ve ortaya çıkan commit'i değiştirilmemiş dizinleri taramadan refs/ws/<actor>/<id>'ye iter.

Saf Git Üzerinde Changelist Modeli: Bellek İçinde Sunucu Tarafı Landing

Geleneksel bir Git iş akışında, bir özellik dalını birleştirmek ya iki üst öğeli bir merge commit oluşturur ve geçmişi doğrusal olmaktan çıkarır ya da geliştiricilerin manuel olarak rebase yapıp main'e eşzamanlı itmelerle yarışmalarını gerektirir.

google3 Changelist (CL) modelinden ilham alan mimarimizdeki her çalışma alanı, trunk snapshot'a karşı tek bir atomik değişikliği temsil eder. ws/alice/optimize-framing içinde yerel olarak çalışırken bir geliştirici ya da ajan on artımlı commit yapabilir (wip, fix tests, lint). Değişiklik ana dala alınmaya hazır olduğunda sunucu, CI runner'ında hiçbir zaman bir çalışma dizini checkout etmeden çalışma alanı deltasını tek bir doğrusal trunk revizyonuna indirger.

Landing betiğinin (land.sh) Git 2.40+ altyapısını kullanarak bellekte nasıl çalıştığı aşağıdadır:

 1# 1. Çalışma alanının dallandığı kök snapshot commit'ini belirleyin
 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. Çalışma ağacı checkout'u olmadan birleştirilmiş kök tree'yi bellekte hesaplayın
 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
  1. adımda:
  • Çalışma alanı dalında kaç yerel commit yapıldığına bakılmaksızın betik yalnızca üç tree karmasını karşılaştırır: özgün temel snapshot ($BASE_TREE), mevcut trunk snapshot'ı ($MAIN_TREE) ve nihai çalışma alanı tree'si ($WORK_TREE).
  • Alice //net/relay'i düzenlerken refs/trunk/main //web/blog'da ilerlediyse, git merge-tree --write-tree birleşik kök tree $NEW_TREE'yi doğrudan Git nesne deposunda milisaniyeler içinde hesaplar; runner'ın dosya sistemine dosya yazmadan.
  • Aşağıdaki politika kontrolleri geçtikten sonra, --force-with-lease ile tek bir git push --atomic refs/trunk/main'i günceller ve refs/trunk/history'ye bir doğrusal commit ekler; landing tetikleyicisi refs/heads/land/<id> silinir.

Mimari Sınırları Uygulamak: Dört Katmanlı Görünürlük ve Hiyerarşik OWNERS

Kodu tek bir depoda birleştirmek, alanlar arası sınırlar bir commit ana dala alınmadan önce mekanik olarak zorunlu kılındığında işe yarar. git merge-tree --write-tree $NEW_TREE'yi ürettikten hemen sonra (ve git push --atomic trunk'ı güncellemeden önce), politika motorumuz git diff-tree "$MAIN_TREE" "$NEW_TREE" üzerinde iki O(delta) kontrolü çalıştırır.

1. Dört Katmanlı Alan Görünürlüğü

Kök tree dört görünürlük katmanına bölünmüştür:

Dizin ÖnekiKatmanİzin Verilen İmportlarYasaklanan İmportlarOWNERS Kapısı
//web/*, //net/*, //ipc/*, //storage/*, //crypto/*, //ai/*, //devtools/*Genel ÜretimGenel Üretim, //third_party/*//corp/*, //experimental/*Zorunlu
//third_party/*Harici TedarikçiKendi kendine yeterli ya da //third_party/*third_party dışındaki tüm alanlarZorunlu
//corp/*Kurumsal ÖzelGenel Üretim, //corp/*, //third_party/*//experimental/*Zorunlu
//experimental/<actor>/*Sandboxcorp olmayan herhangi bir paket//corp/*Muaf (otomatik onaylı)

//web/* içindeki genel bir paket //corp/* içindeki dahili bir paketi ya da //experimental/* içindeki mezun olmamış bir prototipi içe aktarmaya çalışırsa, landing işi refs/trunk/main'e dokunmadan iptal edilir.

2. Gerrit Sunucusu Olmadan Hiyerarşik OWNERS Doğrulaması

Doğrulayıcı, miras kurallarıyla dizin düzeyinde OWNERS dosyalarını uygular:

  • $MAIN_TREE ile $NEW_TREE arasında değiştirilen her yol için doğrulayıcı, dosyanın dizininden //OWNERS'a kadar yukarı doğru yürür.
  • Herhangi bir OWNERS dosyası, üst dizinlerden gelen miras onayları engellemek için set noparent, ya da onayları dosya adı glob'una göre kapsamak için per-file *.sql=@dba-team bildirebilir.
  • Kurcalama Karşıtı Değişmez Kural: Mevcut OWNERS dosyaları her zaman $MAIN_TREE'den (mevcut trunk tree) okunur, asla $NEW_TREE'den okunmaz. Onaylanmamış bir yazar, aynı çalışma alanı commit'inde OWNERS düzenleyerek kendisine izin veremez.
  • Sandbox Muafiyeti: //experimental/<actor>/* altındaki herhangi bir yol OWNERS kontrollerini atlar; böylece mühendisler ve ajanlar git ws-exp <actor> <project> ile serbestçe prototip yapabilir ve daha sonra git ws-graduate ile paketi üretim alanına terfi ettirebilir.

Sürtünmesiz Kod İncelemesi: Geçici PR'lar ve Snapshot Diff'leri

refs/heads/main yalnızca kontrol düzlemini tuttuğundan ve refs/trunk/main üst öğesiz bir commit olduğundan, bir çalışma alanı dalından doğrudan main'e standart bir GitHub Pull Request açmak ilişkisiz geçmişleri karşılaştırmaya çalışırdı. Bunu farklı değişikliklere göre uyarlanmış üç inceleme iş akışıyla çözdük:

  1. Anlık İki Nokta Çalışma Alanı Diff'leri (git ws-save + Kenar Diff Yönlendiricisi) git ws-save ile bir çalışma alanını göndermek, refs/ws/<actor>/<id>'yi hafif bir kenar worker'ı aracılığıyla açığa çıkarır. Bu worker, refs/trunk/main ve çalışma alanı referansını commit SHA'larına çözümler ve GitHub'ın iki nokta karşılaştırma görünümüne (/compare/<trunk-sha>..<ws-sha>) yönlendirir. Küçük yamalar ya da ajan tarafından üretilen değişiklikler için incelemeciler diff'i inceleyebilir ve web arayüzünde doğrudan Land tıklayabilir.
  2. Geçici GitHub Pull Request'leri (git ws-pr) GitHub'da satır satır inceleme için git ws-pr, kök snapshot commit'ini ($(git rev-list --max-parents=0 HEAD)) refs/heads/base/<id>'ye ve çalışma alanı ucunu refs/heads/review/<id>'ye iter. Ardından review/<id>'yi base/<id> ile karşılaştıran bir GitHub Pull Request açar. Temel dal, çalışma alanının dallandığı snapshot olduğundan GitHub'ın Files changed sekmesi yalnızca çalışma alanı deltasını gösterir. İncelemeci Merge pull request tıkladığında bir webhook, head commit'i refs/heads/land/<id>'ye yükseltir, bellek içi landing hattını çalıştırır ve hem refs/heads/base/<id> hem de refs/heads/review/<id>'yi otomatik olarak siler.
  3. Harici Ayna Pull Request'leri Açık kaynak katkıda bulunanların monorepo iş akışını öğrenmesi hiçbir zaman gerekmez. Her zamanki gibi fork'lar oluşturur ve bağımsız ayna depolarına karşı pull request açarlar.

Değişiklikler, Patchset'ler ve Onay Taşıması

Bir dal adı bir değişikliği tanımlayamaz. Bir ajan, ana dala alınmadan önce dalını yeniden adlandırabilir, ucunu amend edebilir ya da daha yeni bir trunk'a birkaç kez rebase yapabilir. Gerrit'te olduğu gibi iş akışı, değişikliği ucun commit mesajındaki Change-Id: I<40 hex> trailer'ı ile tanımlar. git ws-save, git ws-pr ve git ws-land ucda trailer yoksa ekler, varsa korur; böylece kimlik amend, rebase ve yeniden adlandırmalardan sağ çıkar. Ana dala alınan trunk commit'i trailer'ı korur ve inceleme pull request'inin numarasını kaydeder; böylece git trunk-cl <number|Change-Id> onu refs/trunk/history içinde bulur.

İncelemeye gönderilen her uç bir patchset olur. git ws-pr onu, inceleme dallarının yanında değişmez refs/ws/cl/<change-id>/<tip> referansına iter. Bir amend ya da git ws-sync sonrasında git ws-pr'ı yeniden çalıştırmak aynı pull request'e bir patchset ekler. git ws-ps mevcut değişikliğin patchset'lerini listeler ve git ws-psdiff <a> <b> iki patchset arasındaki interdiff'i yazdırır; böylece incelemeci yalnızca son turdan bu yana değişeni okur.

GitHub bir onayı tek bir commit'e bağlar, bu nedenle her rebase normalde yeni bir inceleme gerektirir. Landing hattı ayrıca aynı değişikliğin başka bir patchset'ine verilen onayı kabul eder; her iki patchset de kendi temel snapshot'larına aynı yamayı uyguladığında. Bunu tam ikili diff üzerinde git patch-id --verbatim ile karşılaştırarak kontrol eder. Daha yeni bir trunk'a temiz bir rebase, başka bir inceleme olmadan ana dala alınır; ancak boşluk ya da dosya modu değişikliği dahil her içerik düzenlemesi yeni bir onay gerektirir.

O(delta) Çift Yönlü Senkronizasyon ve Ayrı Üretim Dalları

Birçok kuruluş, dış kullanıcıların monorepo'ya dokunmadan go get, cargo install ya da tek bir projeyi fork'layabilmesi için bireysel paketleri bağımsız GitHub depoları olarak yayımlar. Statik bloglar ya da sürüm kapılı web uygulamaları gibi bazı projeler de main yanında ayrı bir production dalını sürdürür.

Starlark senkronizasyon motorumuz hem dışa aktarımları hem de gelen katkıları, monorepo'nun boyutuyla değil bir commit'te değişen dosya sayısıyla orantılı O(delta) zamanda yönetir:

  • Blobsuz İndeks Ekleme (Blobless Index Splicing): Bir ayna deposunun alt ağacını monorepo kök tree'sine eklerken ya da çıkarırken motor GIT_NO_LAZY_FETCH=1 ayarlar ve git update-index --index-info ardından git write-tree --missing-ok çalıştırır. Değiştirilmemiş alt ağaçlar ve blob'lar SHA-1 ile referanslanmaya devam eder ve CI runner'ına hiçbir zaman indirilmez.
  • Yalnızca Delta Blob Önceden Getirme: Dışa aktarılan bir commit'i bağımsız bir aynaya itmeden önce betik git diff-tree -r --diff-filter=AM "$ext_parent" "$export_tree" çalıştırır ve yalnızca değiştirilen dosyaların eski ve yeni blob'larını git cat-file --batch ile önceden getirir. Git böylece ince bir delta packfile hesaplayıp saniyeler içinde ayna depoya itebilir.
  • İş Akışı ve Sürüm Dalı Koruması: Dışa aktarma sırasında senkronize edici her bağımsız deponun kendi .github/workflows/ dizinini korur; böylece genel CI runner'ları projenin testlerini ayna üzerinde çalıştırır. Starlark yapılandırmasındaki her iş akışı main gibi açık bir dalı hedefler; bu, ayrı bir production dalından dağıtım yapan depolar için sürüm kapılarını korur. Monorepo landing'leri ayna'nın main dalıyla senkronize olur ve main üzerindeki otomatik meta veri ya da çeviri commit'leri webhook aracılığıyla refs/trunk/main'e geri senkronize olur. main'in production'a terfi ettirilmesi açık bir sürüm adımı olarak kalır.

Kapanış Düşünceleri

Bu mimari, Git'in nesne deposunu bir Content-Addressable Store olarak ele alır, refs/heads/main'i refs/trunk/main ve refs/trunk/history'den ayırır, her değişikliği değişmez patchset'lerle birlikte bir Change-Id ile tanımlar ve git merge-tree --write-tree ile ağaçları bellekte birleştirir. Onboarding maliyeti bir görevin ihtiyaç duyduğu dizinlerle birlikte artar; landing ve ayna senkronizasyon maliyeti ise depo boyutuyla değil, değişikliğin boyutuyla birlikte artar.