Das Symptom
Ein Repository wurde geklont, der Hauptzweig ist da, der Build läuft. Dann soll ein zweiter Zweig ausgecheckt werden, und Git kennt ihn nicht. git branch -r zeigt genau einen entfernten Zweig. git log endet nach einem Commit, obwohl das Projekt Jahre alt ist. Ein Werkzeug, das die Versionsnummer aus der Zahl der Commits ableitet, meldet Version 1. git fetch ändert nichts daran, und der Kollege sieht in seinem Klon alle Zweige und die ganze Geschichte.
Typischer Fundort: eine CI-Pipeline oder ein Deploy-Skript, in dem irgendwann jemand –depth 1 ergänzt hat, damit der Klon schneller geht. Oder eine Umgebung, die das als Voreinstellung mitbringt.
Die „Layer 8“-Wahrheit
Die Option –depth tut zwei Dinge, von denen nur eines im Namen steht. Die Dokumentation von git clone formuliert es ohne Umschweife: „Create a shallow clone with a history truncated to the specified number of commits. Implies –single-branch unless –no-single-branch is given to fetch the histories near the tips of all branches.“ Wer die Geschichte auf einen Commit kürzt, bekommt also ungefragt auch einen Klon, der nur einen Zweig kennt. Das ist seit Langem so dokumentiert, es liest nur niemand die Beschreibung einer Option, deren Name so eindeutig klingt.
Die zweite Hälfte ist die, die später beißt. –single-branch beschränkt nicht nur den ersten Klon, sondern prägt die Konfiguration des Remotes: „Further fetches into the resulting repository will only update the remote-tracking branch for the branch this option was used for the initial cloning.“ Deshalb bleibt git fetch wirkungslos, wenn es um andere Zweige geht. Der Server hat sie, der Klon fragt nicht danach. Sichtbar wird das in remote.origin.fetch, wo statt des gewohnten Musters mit Stern ein einzelner Zweigname steht.
Für die fehlende Tiefe gibt es ein dokumentiertes Gegenmittel. git fetch –unshallow macht laut Dokumentation von git fetch aus einem flachen Repository ein vollständiges, „removing all the limitations imposed by shallow repositories“, sofern die Quelle selbst vollständig ist. Wer weniger braucht, nimmt –deepen, das die Grenze von der aktuellen flachen Kante aus um eine Anzahl Commits verschiebt, oder –depth, das vom Spitzencommit aus neu zählt. Ein Detail aus derselben Seite, das bei Release-Werkzeugen wichtig wird: Tags für die nachgeholten Commits werden dabei nicht mitgeholt.
Für die fehlenden Zweige gibt es dagegen keinen Schalter, der das Ein-Zweig-Verhalten nachträglich aufhebt. Entweder holt man einzelne Zweige mit einer Refspec, setzt die Fetch-Zeile des Remotes wieder auf das Muster für alle Köpfe, oder man klont neu und gibt diesmal –no-single-branch mit, was die Dokumentation genau für diesen Zweck nennt.
Die eigentliche Lehre steht im ersten Satz des Zitats: Eine Option, die Zeit spart, spart sie, indem sie etwas weglässt. Ein flacher Klon ist zum Bauen gedacht, nicht zum Entwickeln und nicht zum Vergleichen von Zweigen. Wer in der CI damit arbeitet und später einen Vergleich zum Hauptzweig oder eine Versionsnummer aus der Geschichte braucht, holt gezielt nach, was fehlt, statt sich über einen Server zu wundern, der alles hat, was nicht geholt wurde.