Git bietet verschiedene Ansätze, um die gestellte Aufgabe zu lösen. Hier verwenden wir das git-subtree-Kommando, mit dem die herauszulösenden Teilbäume in Zweige übertragen werden können, welche sich dann wiederum von einem anderen Archiv aus importieren lassen. Quellen waren die Handbuchseite (man git-subtree) und dieser Beitrag auf stackoverflow zusammen mit einigen dort aufgeführten Kommentaren.
Gegeben sei ein Git-Archiv bigrepo.git, dessen Inhalt folgende Verzeichnisstruktur aufweist:
Vielleicht hat es sich nun herausgestellt, das die Dateien unter lib eine Bibliothek ergeben, die nicht nur für dieses Projekt brauchbar, sondern von allgemeinem Interesse ist. Also möchte man daraus ein eigenständiges Projekt machen und die lib-Teilbäume unter main und test in ein separates Git-Archiv auslagern.
Für die folgenden Schritte benötigen wir zunächst eine Arbeitskopie des Archivs. Falls es sich bei bigrepo.git also um ein Bare-Archiv handelt, erzeugen wir im aktuellen Arbeitsverzeichnis eine solche:
git clone /srv/git/bigrepo.git
(unter der Annahme, dass sich bigrepro.git unter /srv/git befindet). Ist kein Bare-Archiv verfügbar, macht man am besten trotzdem eine Kopie, dann vereinfacht man sich am Ende das Aufräumen.
Jetzt wechseln wir in das soeben geklonte neue Archiv und erstellen mit dem Unterbefehl split von git-subtree zwei Zweige – einen für jeden zu extrahierenden Teilbaum –, die wir z. B. split-lib-main und split-lib-test nennen:
cd bigrepo
git subtree split -P src/main/lib -b split-lib-main
git subtree split -P src/test/lib -b split-lib-test
Die beiden erzeugten Zweige enthalten jeweils alle Dateien und Verzeichnisse unterhalb des mit der Option -P angegebenen Pfads inklusive der gesamten Versionsgeschichte; die Pfadangaben hinter -P dürfen keine voranstehenden oder angehängten Schrägstriche enthalten. Die Option -b spezifiziert den Namen des Zweiges, den man hier beliebig wählen kann.
Allerdings gehen bei dieser Vorgehensweise in den neuen Zweigen die Pfadpräfixe verloren. Das heißt, die Dateien und Verzeichnisse etwa in split-lib-main enthalten keine Information mehr darüber, dass sie ursprünglich unter src/main/lib angesiedelt waren. Die darunter liegende Struktur bleibt aber erhalten.
Behalten wir das im Hinterkopf und gehen zur Erzeugung des neuen Archivs – nennen wir es librepo – über, das nur noch die extrahierten Teile enthält. Der Einfachheit halber erstellen wir das neue Archiv auf der gleichen Ebene wie bigrepo. Da wir uns nach den obigen Aktivitäten noch in bigrepo befinden, tun wir zunächst Folgendes:
cd ..
mkdir librepo
cd librepo
git init
Damit haben wir ein leeres Archiv, das die oben extrahierten Dateien aufnehmen kann. Leider funktioniert der sogleich verwendete Befehl in einem leeren Archiv nicht. Daher fügen wir einfach irgendeine Datei ein, z. B. eine, die wir später wahrscheinlich sowieso gebrauchen:
touch .gitignore
git add .gitignore
git commit -m "Ignore file added." .gitignore
Auf diese Weise können wir nun die Extrahierung abschließen:
git subtree add -P src/main/lib ../bigrepo split-lib-main
git subtree add -P src/test/lib ../bigrepo split-lib-test
Über die Option -P fügen wir die verloren gegangenen Teile des Pfads wieder hinzu; stattdessen hätten wir aber auch einen anderen, neuen Pfad angeben können.
Falls es noch einzelne Dateien gibt, die in librepo gehören, aber über diese Methode nicht extrahiert werden können – etwa Konfigurationsdateien für Maven, Lizenz- oder README-Dateien aus dem Stammverzeichnis des Projekts –, so kann man diese am besten manuell hinzufügen (unter Verlust der Versionsgeschichte, was bei solchen Dateien aber wohl nicht problematisch ist).
Unser librepo enthält nun alle Dateien und Verzeichnisse, die in bigrepo unter scr/main/lib und src/test/lib gespeichert sind nebst allen zugehörigen Versionsinformationen. Wir können daraus mit den üblichen Git-Befehlen ein Bare-Repository erzeugen und es an beliebigen Orten ablegen.
An diesem Punkt bietet git-subtree sogar noch die Möglichkeit, die extrahierten Bestandteile in beiden Archiven (bigrepo und librepo) parallel weiterzupflegen. Das ist aber jenseits der beschriebenen Problemstellung und führt ohnehin in aller Regel zu Konsistenzproblemen. Diese Möglichkeit wird daher hier nicht weiter vertieft.
Was also noch bleibt, ist das Aufräumen: Da wir am Anfang eine Kopie des Ursprungsarchivs erstellt haben, können wir diese Kopie einfach wegwerfen. Oder wir können die herausgelösten Zweige zu Referenzzwecken in das Ursprungsarchiv pushen. In jedem Fall sollten wir allerdings die extrahierten Bestandteile aus dem Ursprungsarchiv löschen (nachdem wir uns davon überzeugt haben, dass alles vollständig in librepo enthalten ist, versteht sich), um auseinanderdriftenden Parallelentwicklungen vorzubeugen.