Ein Repository aufzuteilen ist keine Werkzeugfrage. Das Werkzeug dafür ist an einem Nachmittag verstanden. Schwierig ist die Entscheidung davor und die Aufräumarbeit danach, und beides wird regelmäßig übersprungen, weil der eigentliche Schnitt so schnell geht.
Dieser Beitrag ist ein vollständiges Tutorial und setzt nichts voraus außer Git auf der Kommandozeile. Er nimmt einen Werkzeug-Onlineshop, der in einem einzigen Repository gewachsen ist, führt den Moment herbei, in dem die gemeinsame Ablage nicht mehr trägt, macht den Split Schritt für Schritt mit erhaltener Historie und zeigt danach, was am Split-Tag trotzdem schiefgeht. Am Ende steht der ehrliche Gegenvorschlag: wann man besser gar nicht teilt.
Inhalt
- Die Szene: ein Shop, ein Repository
- Warum das lange die richtige Entscheidung war
- Der Auslöser: jemand von außen soll an die Preise
- Der erste Versuch, der harmlos aussieht
- Drei Wochen später fehlt die Antwort
- Die vier Signale, die den Split rechtfertigen
- Die drei Gründe, die keiner sind
- Was du brauchst
- Schritt 1: die Grenze bestimmen
- Schritt 2: den frischen Klon anlegen
- Schritt 3: die Historie herausfiltern
- Schritt 4: die alten Pfade mitnehmen
- Schritt 5: die Tags klären
- Schritt 6: das neue Repository befüllen
- Schritt 7: das alte Verzeichnis stilllegen
- Der Wendepunkt: was am Split-Tag trotzdem schiefgeht
- Was der Split dauerhaft kostet
- Die Werkzeuge im Vergleich
- Die Gegenrichtung: das Monorepo tragfähig halten
- Wann sich der Split lohnt und wann nicht
- Häufige Fragen
- Quellen
- Fazit
Die Szene: ein Shop, ein Repository
Ein Onlineshop verkauft Werkzeug: Bohrmaschinen, Stichsägen, Akkuschrauber, dazu Zubehör und Ersatzteile. Angefangen hat er als eine Anwendung, gewachsen ist er zu fünf Diensten. Alles liegt bis heute in einem Repository.
werkzeugshop/
├── storefront/ Weboberfläche
├── catalog-service/ Artikel, Kategorien, Suche
├── checkout-service/ Warenkorb, Bestellung, Zahlung
├── pricing-service/ Listenpreise, Aktionen, Mengenstaffeln
├── shared-contracts/ gemeinsame DTOs und OpenAPI-Dateien
└── infra/ Terraform, Helm-Charts, PipelinesEin Klon, ein Checkout, eine Pipeline. Wer eine Schnittstelle ändert, ändert im selben Commit auch alle Aufrufer. Der Build ist grün oder rot, und beides gilt für den ganzen Shop.
Warum das lange die richtige Entscheidung war
Es gibt in diesem Aufbau keine Versionsmatrix, weil es nichts zu versionieren gibt. Was im Hauptbranch steht, passt zusammen. Das ist der größte Vorteil eines gemeinsamen Repositories, und er wird meist erst vermisst, wenn er weg ist.
Ein Beispiel aus dem Shop. Die Preis-Engine liefert Preise bisher als Ganzzahl in Cent. Für Ersatzteile mit Kleinstpreisen soll daraus ein Dezimalwert werden. Im gemeinsamen Repository ist das eine Änderung:
// shared-contracts/src/main/java/shop/contracts/PriceDto.java
public record PriceDto(String sku, BigDecimal amount, String currency) { }Der gleiche Commit zieht pricing-service und checkout-service nach. Kein Release der Vertragsbibliothek, keine Übergangsphase mit zwei gültigen Formaten. Ein Review, ein grüner Build, fertig.
Diese Eigenschaft verschwindet nicht, wenn das Repository wächst. Sie wird nur irgendwann von anderen Kosten überholt.
Der Auslöser: jemand von außen soll an die Preise
Der Shop will Mengenstaffeln für Ersatzteile einführen. Ab zwölf Stück ein Preis, ab achtundvierzig ein anderer, dazu Sonderregeln für Händlerkunden. Die Fachlogik dafür ist umfangreicher als gedacht, und im Team ist niemand frei. Also kommt ein externer Dienstleister dazu, der genau diesen Umbau macht.
Damit ist eine Frage plötzlich nicht mehr theoretisch: Er braucht Schreibzugriff auf pricing-service. Er soll aber nicht den checkout-service sehen, in dem die Zahlungsanbindung samt Schlüsselverwaltung liegt, und schon gar nicht deren vollständige Historie.
Git kann das nicht auflösen. Berechtigungen greifen auf Repository-Ebene, nicht auf Verzeichnisebene. Es gibt keine Einstellung, die einem Mitarbeitenden ein Unterverzeichnis freigibt und den Rest verbirgt. Damit ist die Grundsatzdiskussion beendet, und zwar durch eine schlichte Eigenschaft des Werkzeugs.
Das ist der wichtigste Punkt an dieser Stelle: Der Split kommt hier nicht, weil jemand die Struktur schöner findet. Er kommt, weil eine Anforderung besteht, die anders nicht erfüllbar ist.
Der erste Versuch, der harmlos aussieht
Der naheliegende Weg dauert zwei Minuten. Neues Repository anlegen, Ordner hineinkopieren, einmal committen.
mkdir ../pricing-service
cp -r werkzeugshop/pricing-service/* ../pricing-service/
cd ../pricing-service
git init
git add .
git commit -m "initial import"Das Ergebnis funktioniert. Der Dienst baut, die Tests laufen, der Dienstleister bekommt Zugriff, und niemand sieht mehr, als er sehen soll. Für den Moment ist die Aufgabe erledigt.
Was dabei verloren geht, merkt man erst später, und deshalb wirkt dieser Weg so verlockend.
Drei Wochen später fehlt die Antwort
Ein Kunde bestellt vierzehn Sägeblätter und bekommt den Staffelpreis. Bei elf Blättern bekommt er ihn nicht. Die Grenze liegt bei zwölf, und jemand fragt, warum eigentlich zwölf und nicht zehn.
Im alten Repository wäre die Antwort einen Befehl entfernt:
git log -L :calculateTieredPrice:src/main/java/shop/pricing/TierCalculator.javaIm neuen Repository gibt es genau einen Commit, und der heißt „initial import“. Die Diskussion, in der die zwölf entstanden ist, der Verweis auf das Ticket, der Kommentar des Kollegen, der damals auf die Verpackungseinheit hingewiesen hat: alles weg. Nicht gelöscht, denn im alten Repository steht es noch. Aber es steht dort, wo niemand mehr sucht, und es hängt an Pfaden, die es im neuen Repository nicht mehr gibt.
Genau darum geht es beim Split mit Historie. Nicht um Vollständigkeit als Selbstzweck, sondern um die Fähigkeit, in einem Jahr eine Frage zu beantworten.
Die vier Signale, die den Split rechtfertigen
Bevor der technische Teil kommt, die Entscheidung. Vier Beobachtungen tragen den Split, und in der Praxis treten sie fast immer gemeinsam auf.
Unterschiedliche Release-Takte. Die Storefront geht mehrmals täglich live. Der checkout-service fasst Änderungen zu Freigaben zusammen, weil dort Zahlungsabwicklung hängt. Im gemeinsamen Repository zwingt der langsamere Takt dem schnelleren seine Frequenz auf, oder das Team baut Branch-Konstrukte, um beide zu entkoppeln. Wenn diese Konstrukte anfangen, eigene Regeln zu brauchen, ist das das Signal.
Getrennte Verantwortung. Solange ein Team alles verantwortet, ist jedes Review eine fachliche Frage. Sobald zwei Teams zuständig sind, wird daraus eine Frage der Zuständigkeit. Regeln über Codeeigentümer bilden das ab, aber sie erzwingen es nicht.
Zugriff, der sich unterscheiden muss. Das Kriterium aus unserer Szene. Es ist das einzige der vier, über das sich nicht verhandeln lässt.
Rückmeldezeiten, die niemand mehr trägt. Wenn jede Änderung an der Storefront den Checkout mitbaut, wächst die Wartezeit mit dem gesamten Shop statt mit dem geänderten Teil. Bevor man deswegen teilt, gehört allerdings die Pipeline auf den Prüfstand. Sie sollte ohnehin nur bauen, was sich geändert hat.
Die drei Gründe, die keiner sind
Genauso wichtig ist, was den Split nicht rechtfertigt.
Das Repository sei zu groß geworden. Größe ist ein Werkzeugproblem, und Git hat dafür Antworten, die weiter unten stehen. Wer wegen der Klon-Dauer teilt, tauscht eine Unannehmlichkeit gegen eine dauerhafte Koordinationsaufgabe.
Der Ordner sehe unübersichtlich aus. Übersichtlichkeit entsteht durch Modulschnitt. Mit der Anzahl der Remotes hat sie nichts zu tun. Ein unsauber geschnittener Shop wird durch die Verteilung auf fünf Repositories schwerer zu korrigieren, weil die Grenze dann in der Infrastruktur festgeschrieben ist.
Microservices bräuchten je ein Repository. Das ist Konvention. Unabhängig deploybar zu sein hat mit der Ablage des Quelltextes nichts zu tun, und der Shop beweist das seit Jahren.
Was du brauchst
Für die folgenden Schritte reichen Git und ein zusätzliches Werkzeug.
git filter-repo gehört nicht zum Lieferumfang von Git. Es ist ein einzelnes Python-Skript, das man in den Suchpfad legt. Vorausgesetzt werden laut Projekt-Dokumentation Git ab Version 2.36.0 und Python 3 ab Version 3.6.
python3 --version
git --version
git filter-repo --versionAntwortet der letzte Befehl mit „not a git command“, fehlt das Skript noch. Es liegt bei den gängigen Paketverwaltungen bereit und lässt sich alternativ als Datei ablegen und ausführbar machen.
Ein Wort zu git filter-branch, das viele ältere Anleitungen empfehlen: Die Git-Dokumentation rät davon inzwischen ausdrücklich ab. Sie nennt dafür zwei Gründe. Der Befehl beschädigt die Historie auf Weisen, die man nicht sofort bemerkt, und er ist so langsam, dass das Nachstellen eines solchen Schadens zur Geduldsprobe wird. Für unseren Fall ist er damit erledigt.
Schritt 1: die Grenze bestimmen
Vor dem ersten Befehl steht die Frage, was genau herauswandert. Beim Werkzeugshop ist die Antwort nicht einfach pricing-service/, denn der Dienst nutzt gemeinsame Verträge.
grep -rl "shop.contracts" werkzeugshop/pricing-service/src | headEs gibt drei Möglichkeiten, und die Entscheidung gehört vor den Split, nicht danach:
- Die Verträge bleiben im Monorepo und werden künftig als Bibliothek veröffentlicht. Dann braucht der herausgelöste Dienst eine Abhängigkeit auf eine Version, und damit beginnt die Versionsmatrix.
- Die Verträge wandern mit. Dann existieren sie zweimal, und die beiden Kopien driften.
- Die Verträge bekommen ein eigenes Repository. Sauber, aber es ist ein zweiter Split.
Für den Shop fällt die Wahl auf die erste Variante, weil checkout-service und storefront dieselben Verträge brauchen. Der herausgelöste Dienst zieht sie künftig als versioniertes Artefakt.
Halte diese Entscheidung schriftlich fest, bevor du weitermachst. Sie ist der Teil, den man in sechs Monaten nicht mehr rekonstruieren kann.
Schritt 2: den frischen Klon anlegen
git filter-repo schreibt Historie um. Das passiert auf einer Kopie, die für genau diesen Zweck existiert. Das Arbeitsverzeichnis, in dem gerade jemand entwickelt, bleibt unberührt.
git clone https://github.com/werkzeugshop/werkzeugshop.git pricing-split
cd pricing-splitDas Werkzeug bringt dafür eine Vorsichtsmaßnahme mit: Läuft es nicht in einem frischen Klon, bricht es ab, sofern man es nicht ausdrücklich übersteuert. Diese Haltung lohnt sich für die ganze Migration. Ein Klon, der ohnehin nur für den Versuch existiert, darf misslingen. Man löscht ihn und setzt neu an, und niemand verliert dabei etwas.
Bei einem großen Repository lohnt sich hier ein Blick auf die Uhr. Wie lange der Filterlauf dauert, misst du besser an dieser Kopie, statt es am Migrationstag herauszufinden.
Schritt 3: die Historie herausfiltern
Jetzt der eigentliche Schnitt. Der Befehl behält nur ein Verzeichnis und zieht es an die Wurzel des Repositories.
git filter-repo --subdirectory-filter pricing-serviceDanach steht der Inhalt des Dienstes im Wurzelverzeichnis, und die Historie enthält nur noch Commits, die dieses Verzeichnis berührt haben. Prüfen lässt sich das sofort:
ls
git log --oneline | wc -l
git log --oneline | tail -5Die Ausgabe zeigt den Unterschied zum kopierten Ordner von vorhin:
src pom.xml README.md Dockerfile
487
a3f19c2 Preis-Engine aus dem Monolithen herausgelöst
7d4e881 Aktionspreise mit Gültigkeitszeitraum
1c9a03e Rundung auf Verpackungseinheit
0b2e447 Mengenstaffel ab zwölf Stück, Ticket SHOP-412
9f1d330 Erste Fassung ListenpreiseDie Antwort auf die Frage nach der Zwölf steht wieder da, inklusive Ticketnummer. Das Werkzeug räumt im Anschluss selbst auf und packt das Repository neu, dafür ist nichts weiter zu tun.
Schritt 4: die alten Pfade mitnehmen
Hier lauert der Fehler, der am häufigsten unbemerkt bleibt. Pfadfilter arbeiten auf den Pfaden, wie sie in der Historie stehen, nicht auf dem heutigen Namen.
Der Dienst im Werkzeugshop hieß nicht immer pricing-service. In den ersten zwei Jahren lag er unter preise/, danach unter preis-service/, erst seit der Umstellung auf englische Bezeichner heißt er wie heute. Der Befehl aus Schritt 3 kennt nur den aktuellen Namen. Alles, was vor der letzten Umbenennung passiert ist, fällt still heraus. Es gibt keine Warnung, das Ergebnis sieht plausibel aus, und die Historie endet an einem Datum, das niemandem auffällt.
Deshalb gehört vor den Filterlauf ein Blick zurück:
git log --follow --name-only --format="%h" -- pricing-service/pom.xml | grep -v "^$" | tail -20Zeigt sich dort ein früherer Pfad, nimmst du alle Varianten mit und benennst sie in einem Zug um:
git filter-repo
--path preise/
--path preis-service/
--path pricing-service/
--path-rename preise/:pricing-service/
--path-rename preis-service/:pricing-service/
--path-rename pricing-service/:Die ersten drei Angaben bestimmen, was erhalten bleibt. Die drei Umbenennungen führen die historischen Pfade zusammen und heben das Ergebnis an die Wurzel. Eine Umbenennung wählt dabei selbst nichts aus, sie greift nur auf das, was die --path-Angaben ausgewählt haben.
Wichtig ist die Reihenfolge im Kopf: Das hier ist kein zweiter Lauf nach Schritt 3, sondern sein Ersatz. Gefiltert wird genau einmal. Hast du Schritt 3 schon ausgeführt, sind die alten Pfade dort bereits verschwunden, und das Werkzeug verweigert im selben Klon ohnehin einen zweiten Durchgang, weil der jetzt nicht mehr frisch ist. Dann wirfst du den Klon weg und fängst bei Schritt 2 an. Genau dafür ist er da.
Schritt 5: die Tags klären
Der Werkzeugshop hat seine Releases am Gesamtprojekt getaggt: v3.4.0, v3.5.0 und so weiter. Diese Tags wandern beim Filtern mit, und im neuen Repository sind sie irreführend, denn v3.4.0 war nie eine Version der Preis-Engine, sondern eine des Shops.
Zwei Wege sind vertretbar. Entweder du benennst sie beim Filtern um, damit ihre Herkunft sichtbar bleibt. Das hängst du an denselben einen Lauf aus Schritt 4 an:
git filter-repo
--path preise/
--path preis-service/
--path pricing-service/
--path-rename preise/:pricing-service/
--path-rename preis-service/:pricing-service/
--path-rename pricing-service/:
--tag-rename "":"shop-"Aus v3.4.0 wird damit shop-v3.4.0, und niemand verwechselt das später mit einer Version des Dienstes. Oder du wirfst sie hinterher weg und fängst mit eigener Zählung an:
git tag -l | while read -r t; do git tag -d "$t"; done
git tag v1.0.0Für den Shop fällt die Entscheidung auf die Umbenennung, weil in den Release-Notizen des Betriebshandbuchs auf die alten Nummern verwiesen wird. Wichtig ist nur, dass es eine bewusste Entscheidung ist. Unbenannt stehen zu lassen ist die einzige Variante, die später Ärger macht.
Schritt 6: das neue Repository befüllen
Um den Verweis auf das Monorepo musst du dich nicht kümmern, den hat das Werkzeug schon entfernt. Das ist seine zweite eingebaute Sicherheitsmaßnahme, und sie ist absichtlich so gebaut: Ohne origin kann niemand die umgeschriebene Historie versehentlich in das Ursprungsrepository pushen. Wer das doch will, fügt das Remote von Hand wieder hinzu und weiß dann, was er tut.
git remote -v
# Ausgabe leer, filter-repo hat origin entfernt
git remote add origin https://github.com/werkzeugshop/pricing-service.git
git push -u origin main
git push origin --tagsErst danach kommt der Dienstleister dazu, und zwar nur auf diesem Repository. Das war der ganze Zweck der Übung, und an dieser Stelle ist er erreicht.
Schritt 7: das alte Verzeichnis stilllegen
Der Schritt, der am häufigsten ausfällt, weil die eigentliche Arbeit ja getan scheint. Im Monorepo liegt pricing-service/ weiterhin herum. Es baut noch, die Tests laufen noch, und in einem halben Jahr ändert dort jemand eine Zeile, der die Migration nicht mitbekommen hat.
cd ../werkzeugshop
git rm -r pricing-serviceAn die Stelle gehört ein Hinweis, der die Frage beantwortet, bevor sie gestellt wird:
mkdir pricing-service
cat > pricing-service/README.md <<'HINWEIS'
Dieser Dienst ist umgezogen.
Neues Repository: https://github.com/werkzeugshop/pricing-service
Die vollständige Historie ist dort erhalten, auch aus der Zeit als
preise/ und preis-service/.
Hier wird nicht mehr entwickelt.
HINWEIS
git add pricing-service/README.md
git commit -m "Preis-Engine ausgelagert, Verweis auf neues Repository"Dazu gehört, die Pipeline des Monorepos anzupassen, damit sie den verschwundenen Dienst nicht mehr baut, und die Verträge als Bibliothek zu veröffentlichen, wie in Schritt 1 entschieden.
Der Wendepunkt: was am Split-Tag trotzdem schiefgeht
Der Quelltext ist der einfache Teil. Was im Werkzeugshop in den Tagen danach auffiel, hatte mit Git nichts mehr zu tun.
Die Branch-Schutzregeln existierten im neuen Repository nicht. Im Monorepo brauchte jede Änderung am Hauptbranch zwei Freigaben. Das neue Repository war frisch, also galt dort nichts, und der erste Commit des Dienstleisters landete direkt auf main. Niemand hatte etwas falsch gemacht, die Regel war schlicht nie übertragen worden.
Die Secrets fehlten. Die Pipeline des Dienstes brauchte Zugangsdaten zur Artefakt-Registry, und die lagen im Monorepo. Der erste Build im neuen Repository scheiterte, und weil die Fehlermeldung nur von fehlenden Berechtigungen sprach, dauerte die Suche länger als der ganze Split.
Die Aktualisierung der Abhängigkeiten lief ins Leere. Der Bot, der im Monorepo Versionssprünge vorschlägt, kannte das neue Repository nicht. Drei Monate lang kam kein einziger Vorschlag, und das fiel erst bei einer Sicherheitsmeldung auf.
Verweise zeigten ins Nichts. In Tickets, in der Dokumentation und in Commit-Nachrichten standen Links auf Dateien im Monorepo. Die Pfade gibt es dort nicht mehr.
Deshalb gehört auf die Liste für den Split-Tag alles außer dem Quelltext: Schutzregeln, Freigabepflichten, Secrets, Automatisierung, Verweise. Wer nur die Historie mitnimmt, hat die Hälfte übertragen.
Was der Split dauerhaft kostet
Die eigentlichen Kosten fallen nicht am Migrationstag an. Sie fallen jeden Monat danach an, und deshalb werden sie unterschätzt.
Die atomare Änderung ist weg. Erinnere dich an die Umstellung der Preise auf BigDecimal weiter oben, ein Commit im gemeinsamen Repository. Nach dem Split wird daraus eine Folge: erst die Vertragsbibliothek in einer neuen Version veröffentlichen, dann die Preis-Engine darauf heben, dann Checkout und Storefront nachziehen. Zwischendurch existieren zwei gültige Formate, und irgendwer muss entscheiden, wie lange.
Damit braucht jede geteilte Schnittstelle eine Version, eine Kompatibilitätszusage und einen Weg, Brüche anzukündigen. Das ist der eigentliche Preis des Splits, und er ist dauerhaft.
Die Wahrheit über den Gesamtstand verschwindet. Vorher beantwortete der Hauptbranch die Frage, welche Stände zusammenpassen. Nachher braucht es dafür eine eigene Quelle, im Werkzeugshop eine Umgebungsbeschreibung im GitOps-Repository, die festhält, welche Version wo läuft.
Die Pipeline vervielfacht sich. Jedes Repository bringt eine eigene Konfiguration und eigene Secrets mit. Was einmal gepflegt wurde, wird jetzt mehrfach gepflegt, und die Konfigurationen driften ab dem ersten Tag auseinander, wenn niemand sie aktiv zusammenhält.
Die Werkzeuge im Vergleich
| Werkzeug | Installation | Geschwindigkeit | Kann einzelne Dateien | Empfehlung |
|---|---|---|---|---|
git filter-repo |
separates Python-Skript | schnell | ja | der Weg für diesen Fall |
git subtree split |
in Git enthalten | deutlich langsamer | nein, nur Verzeichnisse | für einen einmaligen Schnitt an sauberer Grenze |
git filter-branch |
in Git enthalten | sehr langsam | ja | von der Git-Dokumentation ausdrücklich abgeraten |
git subtree split ist der pragmatische Weg, wenn kein zusätzliches Werkzeug installiert werden darf und die Grenze schon sauber auf einem Verzeichnis liegt:
git subtree split --prefix=pricing-service -b pricing-onlyDas erzeugt einen Branch mit der gefilterten Historie, den man in ein neues Repository pusht. Was es nicht kann: einzelne Dateien herauslösen und historische Pfade zusammenführen. Für den Werkzeugshop mit seinen drei Verzeichnisnamen reicht es deshalb nicht.
Die Gegenrichtung: das Monorepo tragfähig halten
Wenn der einzige Schmerz die Größe ist, gibt es einen billigeren Weg als eine Migration. Git bringt dafür zwei Mechanismen mit, die sich kombinieren lassen.
Ein Teil-Klon holt die Dateiinhalte erst, wenn sie gebraucht werden, statt die ganze Geschichte jeder Datei vorab zu übertragen:
git clone --filter=blob:none https://github.com/werkzeugshop/werkzeugshop.gitErgänzend beschränkt git sparse-checkout den Arbeitsbaum auf die Verzeichnisse, an denen jemand tatsächlich arbeitet:
cd werkzeugshop
git sparse-checkout set pricing-service shared-contracts
lsDanach liegen nur noch die beiden Verzeichnisse plus die Dateien der obersten Ebene im Arbeitsbaum. Der voreingestellte Cone-Modus nimmt dabei bewusst nur Verzeichnisse entgegen und keine beliebigen Muster, was die Auswertung schnell hält.
Zwei Einschränkungen gehören dazu. Die Git-Dokumentation führt git sparse-checkout weiterhin als experimentell, und der zugehörige schlanke Index ist ein eigenes, ebenfalls experimentelles Feature, das externe Werkzeuge irritieren kann. Für Entwicklerrechner ist das trotzdem meist die günstigere Antwort als eine Migration, deren Folgekosten dauerhaft anfallen.
Was diese Mechanismen nicht lösen: das Zugriffsproblem aus unserer Szene. Ein Teil-Klon verbirgt nichts, er lädt nur später. Wer den Checkout-Code nicht sehen darf, darf das Repository nicht klonen.
Wann sich der Split lohnt und wann nicht
Er lohnt sich, wenn Zugriffsrechte sich unterscheiden müssen, denn dafür gibt es keine Alternative. Ebenso, wenn zwei Teile dauerhaft unterschiedliche Release-Takte haben und die Konstrukte, die das im gemeinsamen Repository abbilden, selbst zur Last werden. Der dritte Fall sind getrennte Teams, bei denen jedes Review zur Zuständigkeitsfrage wird.
Er lohnt sich nicht, um Ordnung zu schaffen, nicht wegen der Klon-Dauer und nicht, weil eine Architekturkonvention es nahelegt. In diesen Fällen zahlst du Koordinationskosten für ein Problem, das billiger zu lösen gewesen wäre.
Für den Werkzeugshop war die Antwort eindeutig, aber sie galt nur für einen der fünf Dienste. Storefront, Katalog und Checkout liegen bis heute zusammen, und es gibt keinen Grund, daran etwas zu ändern.
Häufige Fragen
Verliere ich die Historie, wenn ich einen Ordner in ein neues Repository kopiere?
Ja, vollständig. Das neue Repository beginnt mit einem einzigen Commit. Die alte Historie existiert weiter im Ursprungsrepository, hängt dort aber an Pfaden, die es im neuen nicht gibt, und wird faktisch nicht mehr gefunden.
Warum soll ich git filter-branch nicht verwenden?
Weil die Git-Dokumentation ausdrücklich davon abrät. Sie nennt zwei Gründe: Der Befehl beschädigt Historie auf Weisen, die man nicht sofort sieht, und er ist so langsam, dass die Fehlersuche danach unverhältnismäßig wird.
Was passiert, wenn das Verzeichnis früher anders hieß?
Der Pfadfilter kennt nur die Pfade, die du angibst. Alles vor der Umbenennung fällt heraus, ohne Warnung. Du musst alle historischen Pfade angeben und sie per Umbenennung zusammenführen.
Muss ich die Tags mitnehmen?
Nein, aber du musst entscheiden. Tags des Gesamtprojekts sind im herausgelösten Repository irreführend, weil sie eine Version bezeichnen, die es dort nie gab. Entweder umbenennen oder löschen.
Kann ich den Split rückgängig machen?
Den Filterlauf selbst ja, indem du den Klon wegwirfst und neu anfängst. Genau dafür ist der frische Klon da. Was sich nicht zurückdrehen lässt, sind die Gewohnheiten danach: veröffentlichte Versionen, Verweise und alles, was andere schon auf das neue Repository gerichtet haben.
Brauche ich für Microservices getrennte Repositories?
Nein. Unabhängig deploybar zu sein ist eine Eigenschaft der Build- und Deployment-Strecke, nicht der Quelltext-Ablage. Ein Monorepo kann fünf Dienste unabhängig ausliefern.
Wie lange dauert der Filterlauf?
Das hängt an der Zahl der Commits und Dateien. Statt zu schätzen, misst du es an dem frischen Klon, den du ohnehin anlegst. Das ist der eine Fall, in dem die Migration einen kostenlosen Probelauf erlaubt.
Quellen
- Git-Dokumentation zu
git filter-branch, inklusive der Warnung und dem Verweis auf die Alternative - Git-Dokumentation zu
git sparse-checkout, Cone-Modus und Status des schlanken Index - Projekt-Dokumentation von
git filter-repofür Voraussetzungen und Verhalten beim frischen Klon
Alle Befehle und Beispiele in diesem Artikel sind eigene und am beschriebenen Aufbau nachvollzogen.
Fazit
Der Split ist keine Aufräumaktion. Er erkennt eine Grenze an, die fachlich längst da war, und im Werkzeugshop war es der Zugriff, der sie sichtbar gemacht hat.
Wenn du ihn gehst, nimm die Historie mit. Sie kostet dich einen zusätzlichen Arbeitsschritt und beantwortet dir in einem Jahr die Frage, warum die Mengenstaffel bei zwölf Stück ansetzt. Und plane den Tag danach ein: Schutzregeln, Secrets, Automatisierung und Verweise wandern nicht von selbst mit.
Der nächste konkrete Schritt ist klein. Nimm ein Verzeichnis, von dem du glaubst, es müsste eigenständig werden, klone das Repository frisch und lass den Filterlauf einmal durch. Du siehst danach in Minuten, ob die Historie durchgängig ist oder ob irgendwo eine Umbenennung wartet, von der niemand mehr wusste.