2013-07-04 14 views
17

Wir haben eine Reihe von git Repositories, die aufgrund der historischen Aufnahme von binären Testdateien und Java .jar Dateien zu einer unüberschaubaren Größe gewachsen sind.Ist es möglich, ein .git-Repository zu verkleinern, ohne den Verlauf neu zu schreiben?

Wir sind gerade dabei, durch die Ausübung von git filter-branch ing diesen Repositories zu gehen, wieder klonen sie überall, wo sie verwendet werden (aus Dutzenden bis Hunderten von Installationen jeder, je nach dem Repo) und der problems with rewriting history gegeben Ich habe mich gefragt, ob es könnte irgendeine andere Lösung sein.

Idealerweise möchte ich Problemdateien externalisieren, ohne den Verlauf jedes Repositorys neu zu schreiben. Theoretisch sollte das möglich sein, weil Sie die gleichen Dateien mit den gleichen Größen und den gleichen Hashes auschecken und sie nur von einem anderen Ort (einem entfernten Ort als dem lokalen Objektspeicher) beziehen. Leider scheint mir keine der möglichen Lösungen, die ich bisher gefunden habe, dies zu ermöglichen.

Beginnend mit git-annex, die nächstgelegene ich auf eine Lösung für mein Problem How to retroactively annex a file already in a git repo war finden konnte, aber wie bei nur die großen Dateien zu entfernen, erfordert dies die Geschichte neu geschrieben werden, um die ursprünglichen git add in ein git annex add zu konvertieren.

von dort Umzug auf, begann ich bei anderen Projekten auf what git-annex is not aufgelistet suchen, so untersuchte ich git-bigfiles, git-media und git-fat. Leider können wir die git-bigfiles Gabel von git nicht verwenden, da wir ein Eclipse Geschäft sind und eine Mischung aus git und EGit verwenden. Es sieht nicht wie git-media oder git-fat kann tun, was ich will, entweder, während Sie vorhandene große Dateien durch die externen Entsprechungen ersetzen konnten, müssten Sie noch die Geschichte neu schreiben, um groß zu entfernen Dateien, die bereits begangen wurden.

Ist es also möglich, ein .git-Repository zu verkleinern, ohne den Verlauf neu zu schreiben, oder sollten wir zum Plan zurückkehren, git filter-branch und eine ganze Menge von Umsetzungen zu verwenden?


Als beiseite, glauben, dass diese sollte möglich sein, ist aber wahrscheinlich den gleichen Beschränkungen wie die von git aktuellen shallow clone Implementierung gebunden.

Git unterstützt bereits mehrere mögliche Standorte für die gleiche Blob, da jedes gegebene Blob im loose object store (.git/objects) oder in einem pack file (.git/Objekte) könnte so theoretisch würde man nur so etwas wie git-annex müssen einzuhaken auf diesem Niveau eher als höher (dh haben das Konzept eines Downloads auf Anfrage Remote-Blob, wenn Sie mögen). Leider kann ich niemanden finden, der so etwas implementiert oder vorgeschlagen hat.

+0

Soweit ich sagen kann, fragen Sie, wie man Geschichte umschreiben, ohne Geschichte neu schreiben. – alternative

+0

@alternative nicht ganz, ich frage, ob es eine Möglichkeit gibt, das Repository zu verkleinern * ohne * die Geschichte neu zu schreiben. Momentan sieht es so aus als wäre die Verwendung von * seichten Klonen * der einzige Weg, aber die Einschränkungen würden wahrscheinlich nicht gut mit unserem Workflow zusammenpassen und selbst wenn dies der Fall wäre, würden sie nur die lokalen (Klone) Repos verkleinern, nicht die entfernten Repos. –

+0

Die einzige Möglichkeit, das Repository zu "verkleinern", wäre, den Inhalt, den Sie abnehmen, zu löschen - daher das Neuschreiben (weshalb jede Antwort sagt, dass dies nicht möglich ist). Es gibt wirklich keine Probleme mit dem Umschreiben von Verlauf, solange Sie es richtig machen. Und ja, flache Klone würden nur die lokalen Repositories betreffen. – alternative

Antwort

8

Sortieren von. Sie können Git's replace feature verwenden, um den großen aufgeblähten Verlauf beiseite zu legen, damit er nur bei Bedarf heruntergeladen wird. Es ist wie ein oberflächlicher Klon, aber ohne die Einschränkungen eines flachen Klons.

Die Idee ist, dass Sie einen Zweig neu starten, indem Sie einen neuen Root-Commit erstellen, und dann den Tip-Commit des alten Zweigs auswählen. Normalerweise würden Sie den gesamten Verlauf auf diese Weise verlieren (was auch bedeutet, dass Sie die großen .jar Dateien nicht klonen müssen), aber wenn der Verlauf benötigt wird, können Sie die historischen Commits abrufen und git replace verwenden, um sie nahtlos wieder zu verbinden.

Siehe Scott Chacon's excellent blog post für eine detaillierte Erklärung und zu Fuß.

Vorteile dieses Ansatzes:

  • Geschichte nicht geändert wird. Wenn Sie zu einem älteren Commit mit dem großen .jars und allem anderen zurückkehren müssen, können Sie es immer noch tun.
  • Wenn Sie sich die alte Geschichte nicht ansehen müssen, ist die Größe Ihres lokalen Klons nett und klein, und alle frischen Klone, die Sie erstellen, müssen keine Tonnen von meist nutzlosen Daten herunterladen.

Nachteile dieses Ansatzes:

  • Die komplette Geschichte ist standardmäßig — Benutzer müssen durch einige Reifen nicht verfügbar springen auf die Geschichte zu bekommen.
  • Wenn Sie häufig Zugriff auf den Verlauf benötigen, werden Sie die aufgeblähten Commits trotzdem herunterladen.
  • Dieser Ansatz hat immer noch einige der gleichen Probleme wie das Umschreiben der Geschichte. Zum Beispiel sieht, wenn Ihr neues Repository wie folgt aus:

    * modify bar (master) 
    | 
    * modify foo <--replace--> * modify foo (historical/master) 
    |       | 
    * instructions    * remove all of the big .jar files 
              | 
              * add another jar 
              | 
              * modify a jar 
              | 
    

    und jemand hat eine alte abzweigen des historischen Zweig, sie verschmelzen in:

    * merge feature xyz into master (master) 
    |\__________________________ 
    |       \ 
    * modify bar     * add feature xyz 
    |       | 
    * modify foo <--replace--> * modify foo (historical/master) 
    |       | 
    * instructions    * remove all of the big .jar files 
              | 
              * add another jar 
              | 
              * modify a jar 
              | 
    

    dann die großen historischen Commits wieder auftauchen wird in Ihrem Haupt-Repository und du bist zurück, wo du angefangen hast. Beachten Sie, dass dies nicht schlimmer ist als das Neuschreiben des Verlaufs. — jemand könnte versehentlich in den Pre-Rewrite-Commits verschmelzen.

    Dies kann durch Zugabe eines update Haken in Ihrem gemeinsamen Repository keine Schübe abzulehnen gemildert werden, dass die historische Wurzel begehen (n) wieder einführen würde.

+0

Wow, danke Richard, es sieht so aus, als ob es genau das ist, wonach ich gesucht habe.Ich werde sehen, ob ich es nächste Woche zur Arbeit bringen kann und wenn ja, wird es auch ein Häkchen geben ... –

+0

Ah, ich sehe, also schreibt das Beispiel die Geschichte von * recent commits * um die großen zu entfernen historisches Commit, ohne die Geschichte dieser * historischen Commits * neu schreiben zu müssen, verwendet aber 'git replace', um Ihnen zu ermöglichen, die * historischen commits * später zurückzuholen, wenn Sie es benötigen. Also, das ist nicht ganz das, wonach ich suche, aber ich werde mir mehr darüber überlegen, wie ich es nutzen kann, um mein Problem zu lösen. –

+0

Ich wünschte, ich hätte davon gewusst, als wir unsere 'git' Repos aus unserem alten' svn' Repo erstellt haben. Anstatt zwischen einer neuen Epoche ohne Geschichte von 'svn' zu wählen oder unser' git' Repo mit Jahren angesammelten 'svn' Cruft zu beginnen, hätten wir einfach unser gesamtes' svn' Repo in einer Reihe von historischen '' behalten können git' repos und dann 'git replace', um sie zurück zu holen, wenn sie gebraucht wurden. In der Tat, ich frage mich, ob wir noch in der Lage sein könnten, rückblickende 'git replaces' Ziele hinzuzufügen. Interessant, sehr interessant ... –

4

Ich kenne keine Lösung, die das Umschreiben der Geschichte vermeiden würde.

In diesem Fall wird die rpeo mit einem Werkzeug wie BFG- repo cleaner Reinigung ist die einfachste Lösung (leichter, dass git filter-branch).

2

Ich denke ehrlich gesagt nicht über eine Möglichkeit, das zu tun.Wenn Sie darüber nachdenken, was Git Ihnen als Benutzer in Bezug auf die Datenintegrität "verspricht", kann ich mir nicht vorstellen, wie Sie eine Datei aus dem Repository entfernen und denselben Hashwert beibehalten können. Mit anderen Worten, wenn das, was Sie fragen, möglich wäre, dann wäre Git viel weniger zuverlässig ...

8

Nein, das ist nicht möglich - Sie müssen Geschichte neu schreiben. Aber hier sind einige Hinweise dafür:

  • As VonC mentioned: Wenn es Ihr Szenario passt, BFG- repo cleaner verwenden - es ist viel einfacher zu bedienen ist als git filter-branch.
  • Sie müssen nicht erneut klonen! Führen Sie einfach diese Befehle statt git pull und Sie werden in Ordnung (ersetzen origin und master mit Fernbedienung und Zweig):

    git fetch origin 
    git reset --hard origin/master 
    

    Aber beachten Sie, dass im Gegensatz zu git pull Sie alle lokalen Änderungen verlieren, die auf die nicht gedrückt werden Server noch nicht.

  • Es hilft sehr, wenn Sie (oder jemand anderes in ihrem Team) vollständig verstehen, wie git Geschichte sieht, und was git pull, git merge und git rebase (auch als git rebase --onto) tun. Dann geben Sie allen Beteiligten ein schnelles Training, wie Sie mit dieser Umschreibungssituation umgehen können (5-10 Minuten sollten ausreichen, die grundlegenden Dos and Don'ts).
  • Beachten Sie, dass git filter-branch in sich selbst keinen Schaden verursacht, jedoch viele Standardarbeitsabläufe verursacht, die Schaden anrichten können. Wenn die Leute nicht entsprechend handeln und die alte Geschichte verschmelzen, müssen Sie die Geschichte vielleicht nur noch einmal neu schreiben, wenn Sie nicht bald genug bemerken.
  • Sie können verhindern, dass Personen die alte Geschichte verschmelzen (genauer gesagt pushen), indem Sie (5 Zeilen) eine entsprechende update hook auf dem Server schreiben. Überprüfe einfach, ob die Historie des gedrückten Kopfes ein bestimmtes altes Commit enthält.
+0

Danke Chronisch. Das einzige wirkliche Problem beim * nicht * re-cloning ist es, jeden einzelnen Zweig, der lokal verwendet wird, "zurückzusetzen" (um alle lokalen refs für den veralteten Zweig loszuwerden) und "git gc --prune = now --aggressive" auszuführen um das Repo tatsächlich zu verkleinern. Wenn du das machst und der Repo * nicht schrumpft, dann weißt du, dass du irgendwo einen Hinweis verpasst hast. Das Re-Cloning macht alle diese Schritte überflüssig (wir setzen unsere ungefähr 20 'git'-Repos mit' buckminster' ein, so dass * alles * einfach neu geklont werden kann). Leider benutzen wir auch Gitolite, um unsere 'Git'-Repos zu hosten, die den' update'-Hook für den eigenen Gebrauch reservieren. –

+0

Können Sie den 'update'-Hook nicht auf die gleiche Weise erweitern? – Chronial

+0

Ich weiß nicht * Gitolit *, aber [Haken und Gitolit] (http://gitolite.com/gitolite/cust.html#hooks) sagt, dass * Sie alle Haken außer diesen: (alle Repos) Gitolit Reserven installieren können der "update" -Hook *, also werde ich warten müssen, bis unser Gitolit-Experte zurückkommt, um mir zu sagen, ob es einen Weg dafür gibt. –

Verwandte Themen