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).
Generell empfiehlt git-filter-repo
nach der Bereinigung das Pushen in ein neues, leeres Repository. Hier sind zahlreiche Gründe aufgeführt, warum dies sinnvoll ist und viele Probleme vermeidet. Trotzdem kann es vorkommen, dass man in dasselbe Repository pushen will und auch das ist unter Beachtung einiger Hinweise möglich.
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/
# option 1: same repo
mv /tmp/config-backup .git/config
git push origin --force --all
# option 2: new repo
git remote add origin git@github.com:foo/bar-new.git
git push origin --force --all
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:
Auf GitLab ist das Prozedere etwas anders:
mkdir tmp-repo
cd tmp-repo
# option 1: same 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 to main/master"
date
# wait 30 minutes (😱)
date
# Settings > Repository > upload /tmp/commit-map-X
# option 2: new repo
git clone git@gitlab.com:foo/bar.git .
git filter-repo --invert-paths --path wp-content/uploads/
git remote add origin git@gitlab.com:foo/bar-new.git
# Settings > Repository > Protected branches/Protected branches >
# enable "Allowed to force push to main/master"
git push origin --force --all
# Settings > Repository > Protected branches/Protected branches >
# disable "Allowed to force push to main/master"
cd ..
rm -rf tmp-repo
Nach einer weiteren Wartezeit von ~5 Minuten können wir unter Settings > Usage Quotas
den Speicherplatz ansehen:
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:
- "poor man's fresh clone"
rm -rf .git && git clone xxx temp && mv temp/.git ./.git && rm -rf temp
- Bei geänderten Dateien (je nach Anwendungsfall):
git checkout -- .
bzw.git add -A . && git commit -m "Push obscure file changes." && git push
- "start from scratch"
rm -rf repo && git clone xxx .
- "ugly pull with rebase"
git pull -r
- Hier holt man hat immer noch die unbereinigte Historie, überschreibt aber in den meisten Fällen aber nicht mehr versehentlich das Remote-Repository mit der großen lokalen Variante
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 Free | GitLab Free | |
Max file size limit | 100 MB | ∞ |
Max repo size limit | 5.000 MB | ∞ |
Max repo count limit | ∞ | ∞ |
Max overall size limit | ∞ | 5.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 ebenfalls mit Hilfe von git-filter-repo
Repositories einfach verschlanken:
mkdir tmp-repo
cd tmp-repo
git clone git@git.tld.com:foo/bar.git .
cp .git/config /tmp/config-backup
git filter-repo --invert-paths --path wp-content/uploads/
# option 1: same repo
mv /tmp/config-backup .git/config
git push origin --mirror
# login on the remote command line and run in the repo-folder
sudo -u git git reflog expire --expire=now --all
sudo -u git git gc --aggressive --prune=now
# if you face memory limit issues, modify the git configuration
sudo -u git git config --global pack.windowMemory "100m"
sudo -u git git config --global pack.packSizeLimit "100m"
sudo -u git git config --global pack.threads "1"
# if in web ui the size does not change, make a slight
# modification to a file and push again normally
# option 2: new repo
git remote add origin git@git.tld.com:foo/bar-new.git
git push origin --force --all
cd ..
rm -rf tmp-repo
Hier ist speziell der Befehl sudo -u git git gc --aggressive --prune=now
wichtig (das per Cron laufende git gc
hat sonst eine zu lange prune-Time von 2 Wochen).