Anleitung

Git Submodule: hinzufügen, aktualisieren und entfernen

Aktualisiert: · Skillaria.Top

Ein Git Submodule ist ein anderes Repository, das in Ihres eingebettet und auf einen bestimmten Commit festgelegt ist. Sie fügen es mit git submodule add <url> <pfad> hinzu, holen es beim Klonen über git clone --recurse-submodules, aktualisieren es mit git submodule update --remote und entfernen es mit git submodule deinit und git rm.

Ein Git Submodule (Submodul) ist ein eigenständiges Git-Repository, das in einem Verzeichnis Ihres Projekts eingebettet ist. Die wichtigste Eigenschaft: Das übergeordnete Repository speichert nicht die Dateien des Submoduls, sondern eine Referenz auf einen bestimmten Commit darin. Deshalb hat jeder, der das Projekt klont, das Submodul in exakt derselben Version – bis Sie diese selbst ausdrücklich ändern.

Submodule kommen zum Einsatz, wenn Sie eine fremde Bibliothek ohne Paketmanager einbinden, gemeinsamen Code zwischen mehreren Projekten teilen oder ein Theme bzw. eine Firmware in ein eigenes Repository mit eigener Historie auslagern möchten.

Wie Git Submodules aufgebaut sind

Ein Submodul besteht aus drei Teilen. Wer versteht, wie sie zusammenhängen, vermeidet den Großteil der Probleme:

  • Die Datei .gitmodules im Projektwurzelverzeichnis – sie wird committet und beschreibt, welcher Pfad aus welcher URL stammt.
  • Der gitlink-Eintrag im Tree des Commits – ein spezielles Element mit dem Modus 160000, das statt Inhalt den Hash des Submodul-Commits enthält.
  • Das Submodul-Repository selbst – seine Daten liegen in .git/modules/<name> des übergeordneten Repositorys, im Submodul-Verzeichnis liegt nur eine Datei .git mit einem Verweis dorthin.
cat .gitmodules
[submodule "libs/ui-kit"]
	path = libs/ui-kit
	url = https://github.com/example/ui-kit.git

git ls-tree HEAD libs/
160000 commit 8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c	libs/ui-kit

Beachten Sie: Das übergeordnete Repository hält einen Commit fest, keinen Branch. Das ist der grundlegende Unterschied zwischen einem Git Submodule und einer Abhängigkeit nach dem Muster „neueste Version aus main“.

Submodul hinzufügen

git submodule add https://github.com/example/ui-kit.git libs/ui-kit
Cloning into '/home/dev/shop/libs/ui-kit'...
done.

git status
	new file:   .gitmodules
	new file:   libs/ui-kit

git commit -m "ui-kit als Submodul einbinden"

Der Befehl klont das Repository, legt .gitmodules an oder ergänzt sie und fügt den gitlink dem Index hinzu. Danach muss nur noch committet werden. Geben Sie keinen Pfad an, wird das Verzeichnis nach dem Repository benannt.

Soll das Submodul beim Aktualisieren einem bestimmten Branch folgen, geben Sie ihn gleich mit an: git submodule add -b develop <url> <pfad>. Der Branch wird in .gitmodules als branch = develop eingetragen.

Relative URL verwenden

Liegt das Submodul auf demselben Server, können Sie einen relativen Pfad angeben: git submodule add ../ui-kit.git libs/ui-kit. Er wird relativ zur Remote-URL des übergeordneten Repositorys aufgelöst und funktioniert daher sowohl über SSH als auch über HTTPS – je nachdem, wie das Projekt geklont wurde.

Projekt mit Submodulen klonen

Die häufigste Beschwerde: „Geklont, aber der Submodul-Ordner ist leer.“ Standardmäßig lädt git clone keine Submodule herunter. Richtig ist:

git clone --recurse-submodules https://github.com/example/shop.git

Wenn das Projekt bereits ohne Submodule geklont wurde:

git submodule update --init --recursive

--init registriert die Submodule aus .gitmodules in der lokalen .git/config, update lädt sie herunter und wechselt auf den festgehaltenen Commit, --recursive macht dasselbe für Submodule innerhalb von Submodulen.

Submodul aktualisieren

Hier gilt es, zwei Aufgaben zu unterscheiden: „die Version holen, die Kollegen committet haben“ und „auf eine neue Version der Bibliothek wechseln“.

Die im Projekt festgehaltene Version holen

Nach einem git pull im übergeordneten Repository kann sich der gitlink geändert haben, die Dateien des Submoduls aktualisieren sich aber nicht von selbst – git status zeigt modified: libs/ui-kit (new commits). Synchronisieren Sie:

git pull
git submodule update --init --recursive

Damit Sie das nicht jedes Mal von Hand erledigen müssen, schalten Sie die Automatik ein: git config submodule.recurse true. Dann aktualisieren pull, checkout und switch die Submodule selbst. Als Alternative für einen einzelnen Aufruf gibt es git pull --recurse-submodules.

Auf eine neue Version der Bibliothek wechseln

# Variante 1: den neuesten Commit des verfolgten Branches holen
git submodule update --remote libs/ui-kit

# Variante 2: Version manuell auswählen
cd libs/ui-kit
git fetch
git checkout v3.2.0
cd ../..

# In beiden Fällen: neue Version im übergeordneten Repository festhalten
git add libs/ui-kit
git commit -m "ui-kit auf v3.2.0 aktualisieren"

--remote nimmt den Branch aus dem Parameter branch in .gitmodules; fehlt dieser, den Branch, auf den HEAD des Remote-Repositorys des Submoduls zeigt (meist main). Ohne den abschließenden Commit im übergeordneten Repository bekommen Ihre Kollegen die neue Version nicht: Ein Submodul-Update ist immer eine Änderung des gitlinks, die committet werden muss.

Was sich in den Submodulen geändert hat, sehen Sie bequem so:

git diff --submodule=log
git config diff.submodule log    # diese Ausgabe zum Standardformat machen
git submodule status
 8d7c6b5a4f3e2d1c0b9a8f7e6d5c4b3a2f1e0d9c libs/ui-kit (v3.1.0)
+1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b libs/charts (v1.4.0-3-g1a2b3c4)

Das Präfix in git submodule status: - – Submodul nicht initialisiert, + – der aktuelle Commit weicht vom im übergeordneten Repository festgehaltenen ab, U – Merge-Konflikt.

Änderungen im Submodul vornehmen

Nach git submodule update befindet sich das Submodul im Zustand detached HEAD – auf einem bestimmten Commit, nicht auf einem Branch. Wer direkt so committet, verliert die Commits beim nächsten Update leicht. Gehen Sie so vor:

cd libs/ui-kit
git switch main                  # auf einen Branch wechseln
# ... Änderungen ...
git commit -am "Button-Abstände korrigieren"
git push                         # zuerst das Submodul pushen!
cd ../..
git add libs/ui-kit
git commit -m "ui-kit: Button-Abstände korrigiert"
git push

Die Reihenfolge beim Push ist entscheidend. Pushen Sie das übergeordnete Repository vor dem Submodul, verweist das Projekt auf dem Server auf einen Commit, den außer Ihnen niemand herunterladen kann, und bei Kollegen bricht git submodule update mit fatal: remote error: upload-pack: not our ref oder did not contain ab. Schutz bietet git push --recurse-submodules=check (verweigert den Push, wenn das Submodul nicht gepusht ist) oder =on-demand (pusht die Submodule automatisch zuerst).

Submodul entfernen

Früher mussten zum Entfernen mehrere Dateien von Hand bearbeitet werden. In aktuellen Git-Versionen genügen drei Schritte:

git submodule deinit -f libs/ui-kit   # aus .git/config entfernen und Ordner leeren
git rm -f libs/ui-kit                 # gitlink und Eintrag in .gitmodules entfernen
rm -rf .git/modules/libs/ui-kit       # gespeicherte Submodul-Daten löschen
git commit -m "Submodul ui-kit entfernen"

Der letzte Schritt ist für die Korrektheit nicht zwingend, doch ohne ihn kann Git beim erneuten Hinzufügen eines Submoduls unter demselben Pfad meckern, dass das lokale Verzeichnis bereits existiert (A git directory for 'libs/ui-kit' is found locally).

Wenn sich die URL des Submoduls ändert

Ist das Repository umgezogen, korrigieren Sie .gitmodules und synchronisieren die lokale Konfiguration:

git submodule set-url libs/ui-kit https://gitlab.com/example/ui-kit.git
git submodule sync --recursive
git commit -am "Neue Adresse für ui-kit"

Auch Ihre Kollegen müssen nach dem Pull git submodule sync ausführen: Die URL aus .gitmodules wird nur bei init oder sync in ihre .git/config übernommen.

Nützliche Befehle für mehrere Submodule

AufgabeBefehl
Liste und Statusgit submodule status --recursive
Befehl in jedem Submodul ausführengit submodule foreach 'git switch main && git pull'
Download beschleunigengit submodule update --init --jobs 4
Ohne Historie herunterladengit submodule update --init --depth 1
Kurzübersicht in statusgit config status.submoduleSummary true

Typische Probleme mit Submodulen

  • Leerer Ordner nach dem Klonen – init wurde nicht ausgeführt. Lösung: git submodule update --init --recursive.
  • „modified: (new commits)“ direkt nach dem Pull – das übergeordnete Repository zeigt auf einen anderen Commit, die Dateien sind aber alt. Lösung: git submodule update.
  • Submodul versehentlich zurückgesetzt – Sie haben git add . ausgeführt, als das Submodul auf einem alten Commit stand, und den Versionsrückschritt committet. Prüfen Sie vor jedem Commit git diff --submodule.
  • gitlink-Konflikt beim Merge – in zwei Branches wurde das Submodul auf unterschiedliche Commits aktualisiert. Wechseln Sie ins Submodul, wählen Sie den richtigen Commit (oft den neueren, der beide Änderungen enthält), dann git add pfad und schließen Sie den Merge ab.
  • Privates Submodul in der CI – der Build kann das Submodul ohne Zugriffsrechte nicht herunterladen. Geben Sie der CI einen Schlüssel oder Token für das Submodul-Repository; relative URLs vereinfachen die Einrichtung.

Submodule im Team: Absprachen

Die meisten Probleme mit Submodulen entstehen nicht durch Git, sondern dadurch, dass die Beteiligten unterschiedlich vorgehen. Ein paar Regeln, die Sie in der README des Projekts festhalten sollten:

  1. Nur mit --recurse-submodules klonen und bei allen submodule.recurse true aktivieren.
  2. Submodule in einem eigenen Commit mit aussagekräftiger Nachricht aktualisieren: welche Version und warum.
  3. Code des Submoduls nicht im übergeordneten Projekt ändern, ohne separat im Submodul selbst zu committen und zu pushen.
  4. In der CI ausdrücklich git submodule update --init --recursive ausführen und prüfen, dass der Build nicht von lokalen Änderungen abhängt.

Um in der CI zu prüfen, ob alle festgehaltenen Submodul-Commits tatsächlich verfügbar sind, reicht ein frischer Klon: Hat jemand vergessen, das Submodul zu pushen, schlägt der Build beim update-Schritt fehl – und nicht eine Woche später bei einem Kollegen.

git clone --recurse-submodules --jobs 4 "$REPO_URL" build
cd build
git submodule status --recursive

Submodule oder Alternativen?

Submodule bieten exakte Versionskontrolle und getrennte Historien, verlangen aber jedem Beteiligten zusätzliche manuelle Schritte ab. Ist der Code eine gewöhnliche Bibliothek, ist ein Paketmanager einfacher (npm, pip, Composer, Go modules). Soll fremder Code einfach im Projekt liegen und ohne Zusatzbefehle mitgeklont werden, schauen Sie sich git subtree an: Es kopiert die Historie ins Hauptrepository, und Ihre Kollegen müssen nichts davon wissen. Bei vielen zusammenhängenden Services entscheiden sich Teams oft für ein Monorepo. Ein Submodul lohnt sich, wenn der eingebettete Code ein Eigenleben hat – eigene Releases und ein eigenes Team.

Fazit

Ein Git Submodule ist eine Referenz auf einen bestimmten Commit eines anderen Repositorys. Hinzufügen – git submodule add, holen – --recurse-submodules oder update --init, auf eine neue Version aktualisieren – update --remote plus Commit im übergeordneten Repository, entfernen – deinit, git rm und Aufräumen von .git/modules. Denken Sie an den detached HEAD im Submodul und an die Push-Reihenfolge. Fortgeschrittene Git-Werkzeuge üben Sie praktisch im Git-Kurs.

Häufige Fragen

Warum ist ein Submodul immer im detached HEAD?

Das übergeordnete Repository speichert einen Commit-Hash, keinen Branch, und git submodule update wechselt das Submodul genau auf diesen Commit. Für Änderungen wechseln Sie manuell auf einen Branch oder verwenden git submodule update --remote --merge.

Kann ich ein Submodul auf einen Tag festlegen?

Direkt nicht, aber Sie können im Submodul per git checkout auf den Tag wechseln und den resultierenden Commit im übergeordneten Repository committen; in git submodule status erscheint der Tag in Klammern.

Enthält das ZIP-Archiv von GitHub die Submodule?

Nein, die Quellcode-Archive von GitHub enthalten keine Submodul-Inhalte, die Ordner bleiben leer. Verwenden Sie git clone --recurse-submodules.

Was ist der Unterschied zwischen git submodule und git subtree?

Ein Submodule speichert eine Referenz auf einen Commit eines externen Repositorys und erfordert eigene Befehle, um den Code zu holen; subtree kopiert Dateien und Historie direkt ins Hauptrepository.

Kann ich ein Submodul in einen anderen Ordner verschieben?

Ja, mit git mv alter/pfad neuer/pfad: Git aktualisiert .gitmodules und die internen Verweise selbst, Sie müssen nur noch committen.

Das Thema im Video lernen

Dieses Thema wird im Kurs „Git vom Anfänger zum versierten Entwickler“ ausführlich mit Praxis behandelt.

Zum Kurs