git gc –aggressive vs git repack

Ich suche nach Möglichkeiten, um die Größe eines git Repository zu reduzieren. Das Suchen führt mich zu git gc --aggressive meiste Zeit. Ich habe auch gelesen, dass dies nicht der bevorzugte Ansatz ist.

Warum? Was sollte ich beachten, wenn ich gc --aggressive laufen gc --aggressive ?

git repack -a -d --depth=250 --window=250 wird empfohlen gegenüber gc --aggressive . Warum? Wie reduziert das repack die Größe eines Repositories? Auch bin ich mir nicht ganz im Klaren über die Flaggen – --depth und – --window .

Was soll ich zwischen gc und repack wählen? Wann sollte ich gc und neu repack ?

Solutions Collecting From Web of "git gc –aggressive vs git repack"

Heute gibt es keinen Unterschied mehr: git gc --aggressive arbeitet nach dem Vorschlag von Linus aus dem Jahr 2007; siehe unten. Ab Version 2.11 (Q4 2016) verwendet git standardmäßig eine Tiefe von 50. Ein Fenster der Größe 250 ist gut, weil es einen größeren Bereich jedes Objekts abtastet, aber Tiefe bei 250 ist schlecht, weil es jede Kette sehr tief alt anspricht Objekte, die alle zukünftigen Git-Operationen für eine marginal geringere Plattenbenutzung verlangsamen.


Historischer Hintergrund

Linus vorgeschlagen (siehe unten für die vollständige Mailing-Liste Post) mit git gc --aggressive nur, wenn Sie, in seinen Worten, “ein wirklich schlechtes Pack” oder “wirklich schrecklich schlechte Deltas”, aber “fast immer, in anderen Fällen, es ist wirklich eine sehr schlechte Sache zu tun. “Das Ergebnis kann sogar Ihr Repository in einem schlechteren Zustand hinterlassen als zu Beginn!

Der Befehl, den er vorschlägt, dies richtig zu tun, nachdem er “eine lange und involvierte Geschichte” importiert hat, ist

 git repack -a -d -f --depth=250 --window=250 

Dies setzt jedoch voraus, dass Sie unerwünschtes Gunk bereits aus Ihrem Repository-Verlauf entfernt haben und dass Sie die Checkliste zum Verkleinern eines Repositorys in der git filter-branch filial git filter-branch Dokumentation befolgt haben.

git-filter-branch kann verwendet werden, um eine Teilmenge von Dateien zu --index-filter , normalerweise mit einer Kombination aus --index-filter und --subdirectory-filter . Die Leute erwarten, dass das resultierende Repository kleiner ist als das Original, aber Sie brauchen noch ein paar Schritte, um es kleiner zu machen, weil Git versucht, Ihre Objekte nicht zu verlieren, bis Sie es sagen. Stellen Sie zuerst sicher, dass:

  • Sie haben wirklich alle Varianten eines Dateinamens entfernt, wenn ein Blob über seine Lebensdauer verschoben wurde. git log --name-only --follow --all -- filename kann Ihnen helfen, Umbenennungen zu finden.

  • Du hast wirklich alle refs gefiltert: benutze --tag-name-filter cat -- --all beim Aufruf von git filter-branch .

Dann gibt es zwei Möglichkeiten, ein kleineres Repository zu erhalten. Ein sicherer Weg ist das Klonen, das das Original intakt hält.

  • git clone file:///path/to/repo es mit git clone file:///path/to/repo . Der Klon verfügt nicht über die entfernten Objekte. Siehe Git-Klon. (Beachten Sie, dass das Klonen mit einem einfachen Pfad nur alles hart verknüpft!)

Wenn Sie es aus irgendwelchen Gründen nicht klonen wollen, überprüfen Sie stattdessen die folgenden Punkte (in dieser Reihenfolge). Dies ist ein sehr destruktiver Ansatz, also machen Sie ein Backup oder gehen Sie zurück zum Klonen. Du wurdest gewarnt.

  • Entferne die ursprünglichen Refs, die durch git-filter-branch gesichert wurden: say

     git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d 
  • Verlasse alle Reflogs mit git reflog expire --expire=now --all .

  • Garbage sammelt alle nicht referenzierten Objekte mit git gc --prune=now (oder wenn Ihr git gc nicht neu genug ist, um Argumente für --prune zu --prune , verwenden git repack -ad; git prune stattdessen git repack -ad; git prune ).


 Date: Wed, 5 Dec 2007 22:09:12 -0800 (PST) From: Linus Torvalds  To: Daniel Berlin  cc: David Miller , ismail at pardus dot org dot tr, gcc at gcc dot gnu dot org, git at vger dot kernel dot org Subject: Re: Git and GCC In-Reply-To: <4aca3dc20712052111o730f6fb6h7a329ee811a70f28@mail.gmail.com> Message-ID:  References: <4aca3dc20712051947t5fbbb383ua1727c652eb25d7e@mail.gmail.com> <20071205.202047.58135920.davem@davemloft.net> <4aca3dc20712052032n521c344cla07a5df1f2c26cb8@mail.gmail.com> <20071205.204848.227521641.davem@davemloft.net> <4aca3dc20712052111o730f6fb6h7a329ee811a70f28@mail.gmail.com> 

Am 06.12.2007 schrieb Daniel Berlin:

Tatsächlich stellt sich heraus, dass git-gc --aggressive dieses dumme Ding macht, um manchmal Dateien zu packen, egal ob Sie aus einem SVN-Repo konvertiert haben oder nicht.

Absolut. git --aggressive ist meist dumm. Es ist wirklich nur nützlich für den Fall von “Ich weiß, dass ich ein wirklich schlechtes Rudel habe, und ich möchte all die schlechten Verpackungsentscheidungen, die ich getan habe, wegcasting.”

Um dies zu erklären, ist es erklärenswert (ihr wisst es wahrscheinlich, aber lasst mich trotzdem die Grundlagen durchgehen), wie Git-Delta-Ketten funktionieren und wie sie sich von den meisten anderen Systemen unterscheiden.

In anderen SCMs ist eine Delta-Kette im Allgemeinen fest. Es könnte “vorwärts” oder “rückwärts” sein, und es könnte sich ein wenig entwickeln, wenn Sie mit dem Repository arbeiten, aber im Allgemeinen ist es eine Kette von Änderungen an einer einzelnen Datei, die als eine Art einzelner SCM-Entität dargestellt wird. In CVS ist es offensichtlich die *,v Datei, und viele andere Systeme tun ziemlich ähnliche Dinge.

Git macht auch Delta-Ketten, aber es macht sie viel “loser”. Es gibt keine feste Entität. Deltas werden gegen jede beliebige andere Version generiert, die git für einen guten Delta-Kandidaten hält (mit verschiedenen ziemlich erfolgreichen Heuristiken), und es gibt absolut keine harten Gruppierungsregeln.

Das ist im Allgemeinen eine sehr gute Sache. Es ist gut für verschiedene konzeptionelle Gründe ( dh Git intern braucht sich nie wirklich um die gesamte Revisionskette zu sorgen – es denkt überhaupt nicht an Deltas), aber es ist auch großartig, weil man die unflexiblen Delta-Regeln loswerden kann Das git hat überhaupt keine Probleme damit, zwei Dateien miteinander zu merge – es gibt einfach keine willkürlichen *,v “Revisionsdateien”, die eine versteckte Bedeutung haben.

Es bedeutet auch, dass die Wahl der Deltas eine viel offenere Frage ist. Wenn Sie die Delta-Kette auf nur eine Datei beschränken, haben Sie wirklich nicht viele Möglichkeiten, was Sie mit Deltas tun können, aber in Git kann es wirklich ein ganz anderes Problem sein.

Und hier kommt das wirklich schlecht benannte --aggressive ins --aggressive . Während Git generell versucht, Delta-Informationen wiederzuverwenden (weil es eine gute Idee ist und keine CPU-Zeit verschwendet, um alle guten Deltas wiederzufinden, die wir früher gefunden haben) Manchmal möchten Sie sagen: “Beginnen wir von vorne, mit einem unbeschriebenen Blatt, und ignorieren Sie alle vorherigen Delta-Informationen und versuchen Sie, einen neuen Satz von Deltas zu generieren.”

Also – --aggressive ist nicht wirklich aggressiv, sondern es geht darum, CPU-Zeit zu verschwenden, um eine Entscheidung zu treffen, die wir bereits früher getroffen haben!

Manchmal ist das eine gute Sache. Insbesondere einige Import-Tools könnten wirklich furchtbar schlechte Deltas erzeugen. Alles, was git fast-import zum Beispiel verwendet, hat wahrscheinlich nicht viel von einem großartigen Delta-Layout, daher könnte es sich lohnen, zu sagen: “Ich möchte von einem leeren Blatt starten.”

Aber fast immer, in anderen Fällen ist es wirklich eine sehr schlechte Sache zu tun. Es wird CPU-Zeit verschwenden, und vor allem, wenn Sie früher einen guten Job gemacht haben, wird das Endergebnis nicht all die guten Deltas wiederverwenden, die Sie bereits gefunden haben, so dass Sie am Ende viel davon haben werden schlechteres Endergebnis auch!

Ich werde einen Patch an Junio ​​schicken, um die git gc --aggressive Dokumentation zu entfernen. Es kann nützlich sein, aber es ist im Allgemeinen nur nützlich, wenn Sie auf einer sehr tiefen Ebene wirklich verstehen, was es tut, und die Dokumentation hilft Ihnen nicht, das zu tun.

Im Allgemeinen ist inkrementelles git gc der richtige Ansatz und besser als git gc --aggressive . Es wird alte Deltas wiederverwenden, und wenn diese alten Deltas nicht gefunden werden können (der Grund dafür, inkrementellen GC zu machen!), Wird es neue erstellen.

Auf der anderen Seite ist es definitiv wahr, dass ein “anfänglicher Import einer langen und involvierten Geschichte” ein Punkt ist, wo es sich lohnen kann, viel Zeit damit zu verbringen, die wirklich guten Deltas zu finden. Dann wird jeder Benutzer, der jemals danach ist (solange er git gc --aggressive nicht verwendet git gc --aggressive , um es rückgängig zu machen!), Den Vorteil dieses einmaligen Ereignisses erhalten. Gerade für große Projekte mit einer langen Geschichte lohnt es sich daher, etwas mehr Arbeit zu leisten, indem man dem Delta-Code sagt, dass er verrückt werden soll.

Das Äquivalent von git gc --aggressivegit gc --aggressive – aber richtig gemacht – ist (über Nacht) etwas zu tun

 git repack -a -d --depth=250 --window=250 

Wo diese Tiefe Sache nur ist, wie tief die Delta-Ketten sein können (machen Sie sie länger für alte Geschichte – es lohnt sich der Raum Overhead), und die Fenster Sache ist, wie groß ein Objekt Fenster wir wollen jeden Delta-Kandidaten zu scannen.

Und hier könnte es gut sein, dass du die -f Flagge hinzufügen willst (das ist die “drop all deltas”, da du jetzt tatsächlich versuchst sicherzustellen, dass dieser tatsächlich gute Kandidaten findet.

Und dann wird es ewig dauern und einen Tag ( dh ein “Mach es über Nacht” -Ding). Aber das Endergebnis ist, dass jeder stromabwärts von diesem Repository viel bessere Pakete bekommen wird, ohne sich selbst darum kümmern zu müssen.

  Linus 

Wann sollte ich gc & repack verwenden?

Wie ich in ” Git Garbage Collection scheint nicht vollständig zu arbeiten ” erwähnt, reicht ein git gc --aggressive alleine nicht aus.

Die effektivste Kombination wäre git repack , aber auch git prune :

 git gc git repack -Ad # kills in-pack garbage git prune # kills loose garbage 

Hinweis: Git 2.11 (Q4 2016) setzt die Standard-GC-Aggressivität auf 50

Siehe commit 07e7dbf (11 Aug 2016) von Jeff King ( peff ) .
(Zusammengeführt von Junio ​​C Hamano – gitster – in commit 0952ca8 , 21 Sep 2016)

gc : Standard aggressive Tiefe bis 50

git gc --aggressive ” wurde verwendet, um die Delta-Kettenlänge auf 250 zu begrenzen, was viel zu tief ist, um zusätzliche git gc --aggressive und sich nachteilig auf die Laufzeitleistung auswirkt.
Das Limit wurde auf 50 reduziert.

Die Zusammenfassung ist: Der aktuelle Standardwert von 250 spart nicht viel Platz und kostet CPU. Es ist kein guter Kompromiss.

Die ” --aggressive ” Flagge zu git-gc macht drei Dinge:

  1. Verwenden Sie ” -f “, um vorhandene Deltas zu löschen und von Grund auf neu zu berechnen
  2. Verwenden Sie “–window = 250”, um nach Deltas zu suchen
  3. Verwenden Sie “–depth = 250”, um längere Delta-Ketten zu erstellen

Die Punkte (1) und (2) sind gute Übereinstimmungen für ein “aggressives” Umpacken.
Sie bitten den Umpacker, mehr Rechenarbeit zu leisten, in der Hoffnung, ein besseres Paket zu bekommen. Sie bezahlen die Kosten während des Umpackens und andere Vorgänge sehen nur den Vorteil.

Punkt (3) ist nicht so klar.
Längere Ketten zuzulassen bedeutet weniger Einschränkungen für die Deltas, was bedeutet, dass man bessere finden und Platz sparen kann.
Es bedeutet aber auch, dass Operationen, die auf die Deltas zugreifen, längeren Ketten folgen müssen, was sich auf ihre performance auswirkt.
Es ist also ein Kompromiss, und es ist nicht klar, dass der Kompromiss sogar ein guter ist.

(Siehe Commit für das Studium )

Sie können sehen, dass sich die CPU-Einsparungen für reguläre Operationen verbessern, wenn wir die Tiefe verringern.
Aber wir können auch sehen, dass die Platzeinsparungen nicht so groß sind, da die Tiefe höher ist. Das Speichern von 5-10% zwischen 10 und 50 ist wahrscheinlich den CPU-Kompromiss wert. Das Speichern von 1% von 50 auf 100 oder ein weiteres 0,5% von 100 auf 250 ist wahrscheinlich nicht möglich.


Apropos CPU-Speicher, ” git repack ” hat gelernt, die Option --threads= zu akzeptieren und sie an Pack-Objekte zu übergeben.

Siehe commit 40bcf31 (26 Apr 2017) von Junio ​​C Hamano ( gitster ) .
(Zusammengeführt von Junio ​​C Hamano – gitster – in commit 31fb6f4 , 29. Mai 2017)

repack: accept --threads= und übergebe es an pack-objects

Wir tun dies bereits für --window= und --depth= ; Dies hilft, wenn der Benutzer --threads=1 für reproduzierbare Tests erzwingen möchte, ohne von mehreren Threads betroffen zu sein.

Das Problem mit git gc --aggressive ist, dass der Optionsname und die Dokumentation irreführend sind.

Wie Linus selbst in dieser Mail erklärt , was git gc --aggressive tut, ist dies:

Während git im Allgemeinen versucht, Delta-Informationen wiederzuverwenden (weil es eine gute Idee ist, und es keine CPU-Zeit verschwendet, alle guten Deltas wiederzufinden, die wir früher gefunden haben), möchten Sie manchmal sagen “Lassen Sie uns von vorne beginnen leere Tafel und ignoriere alle vorherigen Delta-Informationen und versuche, eine neue Menge von Deltas zu erzeugen “.

Normalerweise ist es nicht notwendig Deltas in Git neu zu berechnen, da Git diese Deltas sehr flexibel bestimmt. Es macht nur Sinn, wenn Sie wissen, dass Sie wirklich, wirklich schlechte Deltas haben. Wie Linus erklärt, fallen in diese Kategorie vor allem Werkzeuge, die git fast-import .

Meistens ist git ziemlich gut darin, nützliche Deltas zu finden und mit git gc --aggressive werden Sie Deltas hinterlassen, die potentiell noch schlimmer sind, während Sie viel CPU-Zeit verschwenden.


Linus beendet seine Mail mit der Schlussfolgerung, dass git repack mit einem großen --depth und – --window die bessere Wahl ist; vor allem, nachdem Sie ein großes Projekt importiert haben und sicherstellen möchten, dass Git gute Deltas findet.

Das Äquivalent von git gc --aggressivegit gc --aggressive – aber richtig gemacht – ist (über Nacht) etwas zu tun

git repack -a -d --depth=250 --window=250

Wo diese Tiefe Sache nur ist, wie tief die Delta-Ketten sein können (machen Sie sie länger für alte Geschichte – es lohnt sich der Raum Overhead), und die Fenster Sache ist, wie groß ein Objekt Fenster wir wollen jeden Delta-Kandidaten zu scannen.

Und hier könnte es gut sein, dass Sie das Flag -f hinzufügen (das ist das “drop all old deltas”), da Sie jetzt tatsächlich versuchen, sicherzustellen, dass dieser tatsächlich gute Kandidaten findet.

Vorsicht. Führen Sie git gc --agressive mit dem Repository aus, das nicht mit Remote synchronisiert ist, wenn Sie keine Backups haben.

Diese Operation erstellt Deltas von Grund auf neu und kann zu Datenverlust führen, wenn sie ordnungsgemäß unterbrochen wird.

Für meine 8GB Computer aggressive gc rannte aus dem Speicher auf 1 GB Repository mit 10k kleine Commits. Als der OOM-Killer den Git-process beendete, hatte ich fast ein leeres Repository, nur ein funktionierender Baum und wenige Deltas überlebten.

Natürlich war es nicht die einzige Kopie des Repositorys, also habe ich es einfach neu erstellt und aus der Ferne gezogen (fetch hat nicht an gebrochenem Repo gearbeitet und bin bei ‘resolving deltas’ Schritt für Schritt blockiert), aber wenn dein Repo ist lokales Repo mit einem einzigen Entwickler ohne Fernbedienungen – sichern Sie es zuerst.