Extract folder from git repository with history (and shrink repository)

Irgendwas ist ja immer, heute will ich aus einem GIT Repository (>200MB) ein Verzeichnis incl. der History extrahieren.

Mit Hilfe von Tante Google werde ich schnell fündig und lege los.

Repository clonen

git clone https://... repository

Branch umschreiben

git filter-branch --prune-empty --tag-name-filter cat --subdirectory-filter FOLDER-NAME -- --all

Mit dem folgenden Befehl schreiben wir jetzt die „Geschichte“ (History) um. Dabei ist folgendes zu beachten:

  • --prune-empty entfernt „leere“ Commits
  • --tag-name-filter cat aktualisiert ggf. Tags
  • -- --all als Basis nehme ich den aktuellen Branch, ich will keinen neuen anlegen

Repository aufräumen – Part I

Jetzt haben wir den aktuellen Branch so umgeschrieben, als wenn er schon immer den Inhalt das angegebenem Verzeichnisses hatte. Das Problem ist nur, die Dateien der anderen Branches, Tags usw. sind immer noch im Repository.

In den meisten HowTos im Netz kommen jetzt die folgenden Tipps:

  • Alle Branches, die man nicht brauch löschen
  • Alle remotes löschen
  • Das „reflog“ „expiren“: git reflog expire --expire=all --all
  • Aufräumen: git gc --aggressive --prune=now
  • schlechte Kommentare, wenn es dann doch nicht geht

Alle Tipps haben in meinem Fall viel Zeit gekostet, am Ende konnte ich das Repository nie auf eine vernünftige Größe bekommen.

Repository aufräumen – Part II

Wie häufig reicht es eben nicht aus, mit einem guten Halbwissen plus den Tipps von Tante Google los zu legen.

Als nächstes habe ich mir ein Zeit genommen, um zu verstehen, was git so alles in seinem Repository speichert und wie. Hier kann ich das Kapitel „Git Internals“ (10.1 – 10.5) empfehlen (Buch ProGit).

Weiterhin war die Doku zu dem Befehl git filter-branch extrem hilfreich. Im Abschnitt „CHECKLIST FOR SHRINKING A REPOSITORY“ habe ich dann gelesen und verstanden wie man ein Repository shrinken kann.

In meinem Fall hatte git filter-branch alle „alten“ Branches und Tags als Original in einem intern Folder gespeichert. So wurden alle alten Commits immer noch referenziert und git gc konnte nix aufräumen.

In meinem Fall habe ich mich für das Clonen entschieden und clone nur den „altuellen“ Branch.

git clone --single-branch --branch develop file:///path/to/repo  repositoy-clean

Auch hier war die original Dokumentation wieder extrem hilfreich: Beim Clonen die Quelle immer als URL, also mit dem Prefix file://… angeben — denn sonst macht git nur Hard-Links und man hat ein gleich großes Repository.

Als nächste habe ich mit den bekannten Mitteln das neue Repository wieder auf den Server geschoben und im alten das Verzeichnis gelöscht.

Zusammenfassung<

  • Mit Git kann jeder Folder, incl. seiner History, aus einem Repository extrahiert werden — ohne dass wir den ganzen Rest an Commits behalten müssen.
  • Und wieder habe ich etwas gelernt: Ich habe eine Ahnung, wie cool git seine interne Datenbank aufbaut und was daraus fürs Aufräumen resultiert.
  • Tante Google hat zwar viele Tipps parat (die leider in vielen Fällen keine Hintergrundinformationen bieten) — und deswegen will ich in Zukunft häufiger die Original-Dokumentation zu Rate ziehen.

Referenzen

  • https://git-scm.com/docs/git-filter-branch
  • https://git-scm.com/docs/git-clone
  • https://git-scm.com/book/en/v2
  • https://help.github.com/articles/splitting-a-subfolder-out-into-a-new-repository