Git Historie bereinigen Teil 2

Sensible Daten oder zu hoher Speicherplatzverbrauch: Es gibt gute Gründe den Wunsch zu hegen, die Git-Historie zu verändern. In diesem Blogbeitrag habe ich erklärt, wie man mit Hilfe von BFG Dateien aus der Git-Historie tilgt. Ein Schwachpunkt von BFG ist die fehlende Unterstützung von direkten Pfaden, sodass man nicht gezielt Dateien oder Ordner in Unterordnern aus der Historie entfernen kann. Damit ist es Zeit, alternative Lösungen anzusehen.


Neben dem offiziell nicht empfohlenen git filter branch zählt git-filter-repo zu dem Werkzeug, die Bereinigung der Historie durchzuführen. Nach einer kurzen Installation analysieren wir zunächst das Repository und finden beispielsweise die größten Ordner in der Historie:

git filter-repo --analyze

Nun werden im Ordner .git/filter-repo/analysis allerhand TXT-Dateien generiert:

  • directories-all-sizes.txt
  • extensions-all-sizes.txt
  • path-all-sizes.txt
  • ...

Es lohnt sich, die Datei directories-all-sizes.txt genauer anzusehen:

=== All directories by reverse size ===

Format: unpacked size, packed size, date deleted, directory name

  4624417043 3796607988 <present> <toplevel>
  4475940396 3778033787 <present> wp-content
  4060236681 3694449320 <present> wp-content/uploads
   305163809   70576241 <present> wp-content/plugins
   123818107   15442735 <present> wp-includes
...

Es kommt oft vor, dass man längst ignorierte und aus dem HEAD entfernte Daten in der Historie liegen hat (beispielsweise den WordPress-Medienordner wp-content/uploads/ oder ein versehentlich gepushter node_modules- oder vendor-Ordner).

Wichtig ist, dass die großen Code-Hosting-Plattformen GitHub und GitLab unterschiedliche Vorgehensweisen empfehlen, die sich teilweise voneinander unterscheiden. Auf GitHub entfernen wir beispielsweise wp-content/uploads/ mit Hilfe der folgenden Schritte mit git-filter-repo aus der Historie:

mkdir tmp-repo
cd tmp-repo
git clone git@github.com:foo/bar.git .
cp .git/config /tmp/config-backup
git filter-repo --invert-paths --path wp-content/uploads
mv /tmp/config-backup .git/config
git push origin --force --all
git push origin --force --tags
# check size locally
git gc && git count-objects -vH
cd ..
rm -rf tmp-repo

Wir können nun auch remote die Größe prüfen (die Änderung der Größe via API sowie in der UI kann bis zu 24h dauern). Dazu öffnet man die Repository-Einstellungen (falls das Repository zu einer Organisation gehört, muss man vorher seinen eigenen Account zur Organisation hinzufügen). Nun sehen wir die Größe:

GitHub: Speicherplatz vor der Bereinigung
GitHub: Speicherplatz nach der Bereinigung

Auf GitLab ist das Prozedere etwas anders:

mkdir tmp-repo
cd tmp-repo
# Settings > General > Advanced > Export project > download tar.gz file into tmp-repo
tar xzf 20*.tar.gz
git clone --bare --mirror project.bundle
cd project.git
git filter-repo --invert-paths --path wp-content/uploads/
cp ./filter-repo/commit-map /tmp/commit-map-1
# copying the commit-map has to be done after every single command from git filter-repo
# you need the commit-map files later
git remote remove origin
git remote add origin git@gitlab.com:foo/bar.git
# Settings > Repository > Protected branches/Protected branches >
# enable "Allowed to force push to main/master"
git push origin --force 'refs/heads/*'
git push origin --force 'refs/tags/*'
git push origin --force 'refs/replace/*'
# Settings > Repository > Protected branches/Protected branches >
# disable "Allowed to force push on main/master"
cd ./../../
rm -rf tmp-repo
date
# wait 30 minutes (😱)
date
# Settings > Repository > upload /tmp/commit-map-X

Nach einer weiteren Wartezeit von ~5 Minuten können wir unter Settings > Usage Quotas den Speicherplatz ansehen:

GitLab: Speicherplatz vor der Bereinigung
GitLab: Speicherplatz nach der Bereinigung

Nach dem Entfernen ist es wichtig, dass alle beteiligten Entwickler bei den letzten Schritten mitwirken: Führt ein User mit seiner eigenen lokalen Kopie nun einen normalen push durch, würde dies dazu führen, dass die großen Dateien wieder in das zentrale Repository wandern. Deshalb empfehlen sich die folgenden 3 Möglichkeiten:

  • rm -rf .git && git clone xxx temp && mv temp/.git ./.git && rm -rf temp && git add -A . && git commit -m "Push obscure file changes (only if needed)." && git push
    ("poor man's fresh clone", in existierendes Repository neu klonen)
  • rm -rf repo && git clone xxx .
    ("start from scratch", die sauberste Variante)
  • git pull -r
    ("pull with rebase", man hat immer noch die unbereinigte Historie, überschreibt aber nicht mehr versehentlich)

Im Zuge der aktuellen Quotas (insbesondere durch die neuen Einschränkungen von GitLab) lohnt es sich allemal, die Größe der Historie seiner Repositories zu prüfen und ggf. zu bereinigen:

GitHub FreeGitLab Free
Max file size limit100 MB
Max repo size limit5.000 MB
Max repo count limit
Max overall size limit5.000 MB

Abschließend lohnt es sich auch, einen Blick auf eine selbst gehostete, kostenlose Variante wie Gitea zu werfen. Mit wenig Aufwand kann man auf einem sehr schlanken Server eine selbstgehostete Git-Instanz (GUI per SSL gesichert, Backup inklusive, Steuerung über mächtige API) hosten, die sich obendrein hervorragend konfigurieren lässt und auch in Sachen Datenschutz überlegen ist. Hier lassen sich übrigens mit denselben Schritten wie oben für GitHub beschrieben Repositories verschlanken; man muss jedoch noch auf dem Server den Befehl sudo -u git git gc --aggressive --prune=now ausführen, damit das Remote-Repository sofort verkleinert wird (das per Cron laufende git gc hat eine prune-Time von 2 Wochen).

Zurück