git gc --aggressive vs git repack


88

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

Warum? Was muss ich beachten, wenn ich renne gc --aggressive?

git repack -a -d --depth=250 --window=250wird über empfohlen gc --aggressive. Warum? Wie wird repackdie Größe eines Repositorys reduziert? Auch mir sind die Flaggen --depthund nicht ganz klar --window.

Was soll ich zwischen gcund wählen repack? Wann soll ich gcund verwenden repack?

Antworten:


76

Heutzutage gibt es keinen Unterschied: git gc --aggressivearbeitet nach dem Vorschlag von Linus aus dem Jahr 2007; siehe unten. Ab Version 2.11 (Q4 2016) ist git standardmäßig auf eine Tiefe von 50 eingestellt. Ein Fenster der Größe 250 ist gut, da es einen größeren Abschnitt jedes Objekts scannt, aber die Tiefe bei 250 ist schlecht, da sich jede Kette auf sehr tief alt bezieht Objekte, die alle zukünftigen Git-Operationen verlangsamen, um die Festplattennutzung geringfügig zu verringern.


Historischer Hintergrund

Linus schlug vor (siehe unten für den vollständigen Beitrag auf der Mailingliste), git gc --aggressivenur dann zu verwenden, wenn Sie in seinen Worten "eine wirklich schlechte Packung" oder "wirklich schrecklich schlechte Deltas" haben, aber "fast immer, in anderen Fällen ist es tatsächlich eine wirklich schlechte." etwas zu tun." Das Ergebnis kann sogar dazu führen, dass sich Ihr Repository in einem schlechteren Zustand befindet als zu Beginn!

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

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

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

git-filter-branch kann verwendet werden, um eine Teilmenge von Dateien zu entfernen, normalerweise mit einer Kombination aus --index-filterund --subdirectory-filter. Die Leute erwarten, dass das resultierende Repository kleiner als das Original ist, aber Sie benötigen ein paar weitere Schritte, um es tatsächlich zu verkleinern, da Git sich bemüht, Ihre Objekte nicht zu verlieren, bis Sie es anweisen. Stellen Sie zunächst sicher, dass:

  • Sie haben wirklich alle Varianten eines Dateinamens entfernt, wenn ein Blob während seiner Lebensdauer verschoben wurde. git log --name-only --follow --all -- filenamekann Ihnen helfen, Umbenennungen zu finden.

  • Sie haben wirklich alle Refs gefiltert: Verwenden Sie --tag-name-filter cat -- --allbeim Aufrufen git filter-branch.

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

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

Wenn Sie es aus irgendeinem Grund wirklich nicht klonen möchten, überprüfen Sie stattdessen die folgenden Punkte (in dieser Reihenfolge). Dies ist ein sehr destruktiver Ansatz. Erstellen Sie also ein Backup oder kehren Sie zum Klonen zurück. Du wurdest gewarnt.

  • Entfernen Sie die Original-Refs, die von git-filter-branch gesichert wurden: say

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

  • Garbage sammelt alle nicht referenzierten Objekte mit git gc --prune=now(oder wenn Sie git gcnicht neu genug sind, um Argumente zu unterstützen --prune, verwenden Sie git repack -ad; git prunestattdessen).


Date: Wed, 5 Dec 2007 22:09:12 -0800 (PST)
From: Linus Torvalds <torvalds at linux-foundation dot org>
To: Daniel Berlin <dberlin at dberlin dot org>
cc: David Miller <davem at davemloft dot net>,
    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: <alpine.LFD.0.9999.0712052132450.13796@woody.linux-foundation.org>
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 6. Dezember 2007 schrieb Daniel Berlin:

Tatsächlich stellt sich heraus, dass git-gc --aggressivedies manchmal dumm ist, Dateien zu packen, unabhängig davon, ob Sie von einem SVN-Repo konvertiert haben oder nicht.

Absolut. git --aggressiveist meistens dumm. Es ist wirklich nur für den Fall nützlich: "Ich weiß, dass ich eine wirklich schlechte Packung habe, und ich möchte alle schlechten Packungsentscheidungen, die ich getroffen habe, wegwerfen."

Um dies zu erklären, lohnt es sich zu erklären (Sie sind sich wahrscheinlich dessen bewusst, aber lassen Sie mich trotzdem die Grundlagen durchgehen), wie Git-Delta-Ketten funktionieren und wie sie sich von den meisten anderen Systemen unterscheiden.

In anderen SCMs ist im Allgemeinen eine Delta-Kette festgelegt. Es kann "vorwärts" oder "rückwärts" sein und sich während der Arbeit mit dem Repository etwas weiterentwickeln, aber im Allgemeinen handelt es sich um eine Kette von Änderungen an einer einzelnen Datei, die als eine Art einzelne SCM-Entität dargestellt wird. In CVS ist es offensichtlich die *,vDatei, und viele andere Systeme tun ziemlich ähnliche Dinge.

Git macht auch Delta-Ketten, aber es macht sie viel "lockerer". Es gibt keine feste Einheit. Deltas werden gegen jede andere zufällige Version generiert, die git als guten Delta-Kandidaten erachtet (mit verschiedenen ziemlich erfolgreichen Heuristiken), und es gibt absolut keine harten Gruppierungsregeln.

Dies ist im Allgemeinen eine sehr gute Sache. Es ist aus verschiedenen konzeptionellen Gründen gut ( dh Git muss sich intern nie wirklich um die gesamte Revisionskette kümmern - es denkt überhaupt nicht wirklich in Deltas), aber es ist auch großartig, weil es bedeutet, die unflexiblen Delta-Regeln loszuwerden Dieser Git hat zum Beispiel überhaupt keine Probleme beim Zusammenführen von zwei Dateien - es gibt einfach keine willkürlichen *,v„Revisionsdateien“, die eine versteckte Bedeutung haben.

Dies 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 sollen, aber in Git kann es wirklich ein ganz anderes Problem sein.

Und hier kommt der wirklich schlecht benannte ins --aggressiveSpiel. Während Git im Allgemeinen versucht, Delta-Informationen wiederzuverwenden (weil es eine gute Idee ist und keine CPU-Zeit damit verschwendet, alle guten Deltas wiederzufinden, die wir zuvor gefunden haben), manchmal Sie Ich möchte sagen: "Beginnen wir von vorne mit einer leeren Tafel und ignorieren alle vorherigen Delta-Informationen und versuchen, einen neuen Satz von Deltas zu generieren."

Es geht also --aggressivenicht wirklich darum, aggressiv zu sein, sondern darum, CPU-Zeit zu verschwenden, um eine Entscheidung zu wiederholen, die wir bereits zuvor getroffen haben!

Manchmal ist das eine gute Sache. Insbesondere einige Importwerkzeuge könnten wirklich schrecklich schlechte Deltas erzeugen. Alles git fast-import, was zum Beispiel verwendet wird, hat wahrscheinlich nicht viel von einem großartigen Delta-Layout, daher könnte es sich lohnen zu sagen: "Ich möchte von einer sauberen Tafel ausgehen."

Aber in anderen Fällen ist es fast immer eine wirklich schlechte Sache. Es wird CPU-Zeit verschwenden, und besonders wenn Sie beim Deltaing früher gute Arbeit geleistet haben, wird das Endergebnis nicht alle guten Deltas wiederverwenden, die Sie bereits gefunden haben, sodass Sie am Ende tatsächlich viel haben schlechteres Endergebnis auch!

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

Inkrementell git gcist im Allgemeinen der richtige Ansatz und besser als inkrementell git gc --aggressive. Alte Deltas werden wiederverwendet, und wenn diese alten Deltas nicht gefunden werden können (der Grund für die erstmalige inkrementelle GC!), Werden neue erstellt.

Auf der anderen Seite ist es definitiv wahr, dass ein „anfänglicher Import einer langen und involvierten Geschichte“ ein Punkt ist, an dem es sich lohnen kann, viel Zeit damit zu verbringen, die wirklich guten Deltas zu finden. Dann git gc --aggressiveerhält jeder Benutzer (solange er es nicht zum Rückgängigmachen verwendet!) Den Vorteil dieses einmaligen Ereignisses. Insbesondere für große Projekte mit einer langen Geschichte lohnt es sich wahrscheinlich, zusätzliche Arbeit zu leisten und dem Delta-Finding-Code zu sagen, dass er wild werden soll.

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

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

Bei dieser Tiefe geht es nur darum, wie tief die Delta-Ketten sein können (verlängern Sie sie für die alte Geschichte - es lohnt sich der Platzaufwand), und bei der Fenstersache geht es darum, wie groß ein Objektfenster ist, das jeder Delta-Kandidat scannen soll.

Und hier möchten Sie vielleicht die -fFlagge hinzufügen (dies ist das "alle alten Deltas fallen lassen", da Sie jetzt tatsächlich versuchen, sicherzustellen, dass diese tatsächlich gute Kandidaten findet.

Und dann wird es ewig und einen Tag dauern ( dh eine Sache, die man über Nacht macht). Das Endergebnis ist jedoch, dass jeder, der diesem Repository nachgeschaltet ist, viel bessere Pakete erhält, ohne sich selbst darum kümmern zu müssen.

          Linus

2
Ihr Kommentar zur Tiefe ist etwas verwirrend. Zuerst wollte ich mich beschweren, dass Sie absolut falsch liegen, dass Aggressivität ein Git-Repository erheblich beschleunigen kann. Nach einer aggressiven Garbage Collection wurde ein RIESIGES Repo, das fünf Minuten dauerte, um einen Git-Status zu erstellen, auf Sekunden reduziert. Aber dann wurde mir klar, dass du nicht meinst, dass der aggressive GC das Repo verlangsamt, sondern nur eine extrem große Tiefe.
user6856

57

Wann sollte ich gc & repack verwenden?

Wie ich in " Git Garbage Collection scheint nicht vollständig zu funktionieren " erwähnt habe, git gc --aggressiveist a für sich allein weder ausreichend noch ausreichend.
Und, wie ich weiter unten erläutere , oft nicht benötigt.

Die effektivste Kombination wäre das Hinzufügen 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 Standardtiefe gc aggressiveauf 50

Siehe Commit 07e7dbf (11. August 2016) von Jeff King ( peff) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 0952ca8 , 21. September 2016)

gc: Standardmäßige aggressive Tiefe auf 50

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

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

Die " --aggressive" Flagge git-gcmacht drei Dinge:

  1. Verwenden Sie " -f", um vorhandene Deltas zu entfernen 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) passen gut zu einem "aggressiven" Umpacken.
Sie fordern das Umpacken auf, mehr Rechenarbeit zu leisten, in der Hoffnung, ein besseres Paket zu erhalten. Sie zahlen die Kosten während des Umpackens, und andere Vorgänge sehen nur den Nutzen.

Punkt (3) ist nicht so klar.
Das Zulassen längerer Ketten bedeutet weniger Einschränkungen für die Deltas, was bedeutet, möglicherweise bessere zu finden und Platz zu sparen.
Es bedeutet aber auch, dass Operationen, die auf die Deltas zugreifen, längeren Ketten folgen müssen, was sich auf ihre Leistung auswirkt.
Es ist also ein Kompromiss, und es ist nicht klar, dass der Kompromiss sogar ein guter ist.

(Siehe Commit für Studie )

Sie können sehen, dass sich die CPU-Einsparungen für den regulären Betrieb verbessern, wenn wir die Tiefe verringern.
Wir können aber auch feststellen, dass die Platzersparnis mit zunehmender Tiefe nicht so groß ist. Das Einsparen von 5-10% zwischen 10 und 50 ist wahrscheinlich den CPU-Kompromiss wert. Eine Einsparung von 1% von 50 auf 100 oder weitere 0,5% von 100 auf 250 ist wahrscheinlich nicht möglich.


Apropos CPU-Einsparung: " git repack" hat gelernt, die --threads=<n>Option zu akzeptieren und an Packobjekte zu übergeben.

Siehe Commit 40bcf31 (26. April 2017) von Junio ​​C Hamano ( gitster) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit 31fb6f4 , 29. Mai 2017)

Umpacken: Akzeptieren --threads=<n>und weitergeben anpack-objects

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


3
Ich erwähnte den Linus-Thread im Link "Git Garbage Collection scheint nicht vollständig zu funktionieren"
VonC

1
Danke für dieses moderne Update! Jede andere Antwort hier ist alt. Jetzt können wir sehen, dass git gc --aggressivedies zweimal behoben wurde: Erstens, um das zu tun, was Linus 2007 als "bessere Verpackungsmethode" vorgeschlagen hat. Und dann in Git 2.11, um die von Linus vorgeschlagene übermäßige Objekttiefe zu vermeiden, die sich jedoch als schädlich herausstellte (verlangsamt alle zukünftigen Git-Operationen und spart keinen nennenswerten Platz).
GW0

git gc, gefolgt von git repack -Ad und git prune vergrößern mein Repository ... warum?
Devops

@devops Nicht sicher: Welche Version von Git verwenden Sie? Sie können eine neue Frage dazu stellen (mit weiteren Details wie dem Betriebssystem, der allgemeinen Größe Ihres Repos, ...)
VonC

man git-repacksagt für -d: `Führen Sie auch git prune-packed aus, um redundante lose Objektdateien zu entfernen .` Oder macht das git pruneauch? man git-prunesagt In most cases, users should run git gc, which calls git prune., was nützt es danach git gc? Wäre es nicht besser oder ausreichend, nur zu verwenden git repack -Ad && git gc?
Jakob

14

Das Problem dabei git gc --aggressiveist, dass der Optionsname und die Dokumentation irreführend sind.

Wie Linus selbst in dieser Mail erklärt , git gc --aggressiveist dies im Wesentlichen Folgendes:

Während git im Allgemeinen versucht, Delta-Informationen wiederzuverwenden (weil es eine gute Idee ist und keine CPU-Zeit damit verschwendet, alle guten Deltas wiederzufinden, die wir zuvor gefunden haben), möchten Sie manchmal sagen: "Fangen wir von vorne an, mit a Leere Tafel, ignoriere alle vorherigen Delta-Informationen und versuche, einen neuen Satz von Deltas zu generieren. "

Normalerweise besteht keine Notwendigkeit, Deltas in Git neu zu berechnen, da Git diese Deltas als sehr flexibel bestimmt. Es macht nur Sinn, wenn Sie wissen, dass Sie wirklich, wirklich schlechte Deltas haben. Wie Linus erklärt, fallen hauptsächlich Werkzeuge, die davon Gebrauch machen, git fast-importin diese Kategorie.

Meistens leistet git ziemlich gute Arbeit bei der Ermittlung nützlicher Deltas. Wenn Sie diese verwenden git gc --aggressive, erhalten Sie Deltas, die möglicherweise noch schlimmer sind, während Sie viel CPU-Zeit verschwenden.


Linus beendet seine Mail mit der Schlussfolgerung, dass git repackmit einem großen --depthund --windowin den meisten Fällen die bessere Wahl ist; vor allem nachdem du ein großes projekt importiert hast und sicherstellen willst, dass git gute deltas findet.

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

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

Bei dieser Tiefe geht es nur darum, wie tief die Delta-Ketten sein können (verlängern Sie sie für die alte Geschichte - es lohnt sich der Platzaufwand), und bei der Fenstersache geht es darum, wie groß ein Objektfenster ist, das jeder Delta-Kandidat scannen soll.

Und hier möchten Sie vielleicht die -fFlagge hinzufügen (die "alle alten Deltas fallen lassen" ist, da Sie jetzt tatsächlich versuchen, sicherzustellen, dass diese tatsächlich gute Kandidaten findet.


8

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

Dieser Vorgang erstellt Deltas von Grund auf neu und kann bei ordnungsgemäßer Unterbrechung zu Datenverlust führen.

Für meinen 8-GB-Computer ging dem aggressiven GC der Speicher im 1-GB-Repository mit 10.000 kleinen Commits aus. Als der OOM-Killer den Git-Prozess beendete, blieb mir ein fast leeres Repository übrig, 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 von der Fernbedienung abgerufen (Fetch funktionierte nicht bei defektem Repo und war einige Male beim Schritt "Deltas auflösen" festgefahren), aber wenn Ihr Repo es ist lokales Repo für einzelne Entwickler ohne Fernbedienungen - sichern Sie es zuerst.


5

Hinweis: Achten Sie auf die Verwendung git gc --aggressive, wie in Git 2.22 (Q2 2019) erläutert.

Sehen Sie verpflichten 0044f77 , begehen daecbf2 , begehen 7.384.504 , begehen 22d4e3b , begehen 080a448 , begehen 54d56f5 , begehen d257e0f , begehen b6a8d09 (7. April 2019) und begehen fc559fb , begehen cf9cd77 , begehen b11e856 (22. März 2019) von Ævar Arnfjord Bjarmason ( avar) .
(Zusammengeführt von Junio ​​C Hamano - gitster- in Commit ac70c53 , 25. April 2019)

gc docs: Spielen Sie die Nützlichkeit von herunter --aggressive

In den vorhandenen " gc --aggressive" Dokumenten wird den Benutzern nur empfohlen, sie regelmäßig auszuführen.
Ich habe persönlich mit vielen Benutzern gesprochen, die diese Dokumente als Ratschlag zur Verwendung dieser Option genommen haben, und normalerweise ist dies (meistens) Zeitverschwendung .

Lassen Sie uns also klären, was es wirklich tut, und den Benutzer seine eigenen Schlussfolgerungen ziehen lassen.

Lassen Sie uns auch die "[...] anhaltenden Effekte" klarstellen, um eine kurze Version von Jeff Kings Erklärung zu paraphrasieren .

Das bedeutet, dass die git-gc-Dokumentation jetzt Folgendes enthält :

AGGRESSIV

Wenn die --aggressiveOption angegeben ist, git-repackwird sie mit dem -fFlag aufgerufen , das wiederum --no-reuse-deltaan Git-Pack-Objekte übergeben wird .
Dadurch werden vorhandene Deltas weggeworfen und neu berechnet, was viel mehr Zeit für das Umpacken kostet.

Die Auswirkungen sind meistens anhaltend, z. B. wenn Packungen und lose Objekte zu einer Packung verschmolzen werden, werden die vorhandenen Deltas in dieser Packung möglicherweise wiederverwendet, aber es gibt auch verschiedene Fälle, in denen wir möglicherweise ein suboptimales Delta aus einem neueren auswählen stattdessen packen.

Darüber hinaus werden durch die Lieferung --aggressivedie --depthund --windowOptionen , an die übergeben wurde , optimiert git-repack.
Siehe die gc.aggressiveDepthund gc.aggressiveWindowEinstellungen unten.
Durch die Verwendung eines größeren Fensters finden wir mit größerer Wahrscheinlichkeit optimalere Deltas.

Es lohnt sich wahrscheinlich nicht, diese Option in einem bestimmten Repository zu verwenden, ohne darauf maßgeschneiderte Leistungsbenchmarks auszuführen .
Es dauert viel länger und die daraus resultierende Raum- / Delta-Optimierung kann sich lohnen oder auch nicht. Wenn Sie dies überhaupt nicht verwenden, ist dies für die meisten Benutzer und ihre Repositories der richtige Kompromiss.

Und ( Commit 080a448 ):

gcdocs: Beachten Sie, wie sich & --aggressiveauswirkt--window--depth

Seit 07e7dbf ( gc: aggressive Standardtiefe bis 50, 11.08.2016, Git v2.10.1) verwenden wir etwas verwirrenderweise dieselbe Tiefe --aggressivewie standardmäßig.

Wie in diesem sinnvollen Commit erwähnt, war es falsch, mehr Tiefe als Standard für "aggressiv" festzulegen und so Speicherplatz auf Kosten der Laufzeitleistung zu sparen, was normalerweise das Gegenteil von jemandem ist, der "aggressive gc" möchte. will.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.