1129 Stimmen

Wie entfernt/löscht man eine große Datei aus dem Commit-Verlauf im Git-Repository?

Ich habe versehentlich einen DVD-Rip in ein Website-Projekt eingefügt und dann achtlos git commit -a -m ... und, zack, war das Repo um 2,2 Gigabyte aufgebläht. Beim nächsten Mal nahm ich einige Änderungen vor, löschte die Videodatei und übertrug alles, aber die komprimierte Datei ist immer noch im Repository, in der Historie, vorhanden.

Ich weiß, dass ich von diesen Commits aus Zweige starten und einen Zweig auf einen anderen umbinden kann. Aber was sollte ich tun, um die 2 Commits zusammenzuführen, so dass die große Datei nicht in der Historie angezeigt wird und in der Garbage Collection-Prozedur bereinigt wird?

13 Stimmen

Dieser Artikel soll Ihnen helfen help.github.com/removing-sensitive-data

1 Stimmen

Beachten Sie, dass Sie den vollständigen relativen Pfad angeben müssen, wenn sich Ihre große Datei in einem Unterverzeichnis befindet.

863voto

Roberto Tyley Punkte 22905

Verwenden Sie die BFG Repo-Reiniger , eine einfachere und schnellere Alternative zu git-filter-branch speziell für die Entfernung unerwünschter Dateien aus dem Git-Verlauf entwickelt.

Befolgen Sie sorgfältig die Benutzungsanleitung Der Kernpunkt ist einfach der folgende:

$ java -jar bfg.jar --strip-blobs-bigger-than 100M my-repo.git

Alle Dateien mit einer Größe von mehr als 100 MB (die sich nicht in Ihrem neueste commit) wird aus der Historie Ihres Git-Repositorys entfernt. Sie können dann git gc um die toten Daten zu entfernen:

$ git gc --prune=now --aggressive

Die BFG ist in der Regel mindestens 10-50x schneller als Laufen git-filter-branch und ist im Allgemeinen einfacher zu bedienen.

Vollständige Offenlegung: Ich bin der Autor des BFG Repo-Cleaner.

0 Stimmen

@Roberto: Ich habe die Anweisungen auf der Website befolgt und einen Klon --mirror ausgeführt. Als es an der Zeit war, das Repo zu pushen, schlug es mit der Aussage fehl, dass ich zuerst pullen müsse. Ich bin mir ziemlich sicher, dass es zwischen dem Klonen und dem Zurückschieben keine Änderungen gegeben hat. Wenn ich einen Pull-Vorgang durchführe, beschwert sich Git, dass es einen Arbeitsbaum in my-repo.git benötigt. Irgendwelche Vorschläge?

6 Stimmen

@tony Es lohnt sich, den gesamten Vorgang des Klonens und Löschens zu wiederholen, um zu sehen, ob die Meldung, die Sie zum Ziehen auffordert, erneut auftritt, aber das liegt mit ziemlicher Sicherheit daran, dass Ihr entfernter Server so konfiguriert ist, dass er Aktualisierungen, die nicht schnell weitergeleitet werden, ablehnt (d. h. er ist so konfiguriert, dass er verhindert, dass Sie den Verlauf verlieren - was genau das ist, was Sie tun wollen). Sie müssen diese Einstellung auf dem Remoteserver ändern oder, falls das nicht möglich ist, die aktualisierte Projektarchivgeschichte in ein neues leeres Projektarchiv pushen.

1 Stimmen

@RobertoTyley Danke. Ich habe es 3 Mal versucht, und alle Male kam die gleiche Meldung. Ich denke also, dass Sie Recht damit haben, dass der Remote-Server so konfiguriert ist, dass er die nicht schnell weitergeleiteten Updates ablehnt. Ich werde in Erwägung ziehen, das aktualisierte Repo einfach in ein neues Repo zu verschieben. Ich danke Ihnen!

713voto

Greg Bacon Punkte 127209

Was Sie vorhaben, ist äußerst störend, wenn Sie die Geschichte an andere Entwickler weitergegeben haben. Siehe "Wiederherstellung von Upstream Rebase" in der git rebase Dokumentation für die notwendigen Schritte nach der Reparatur Ihrer Geschichte.

Sie haben mindestens zwei Möglichkeiten: git filter-branch und ein interaktive Umbasierung , die beide weiter unten erläutert werden.

Verwendung von git filter-branch

Ich hatte ein ähnliches Problem mit sperrigen binären Testdaten aus einem Subversion-Import und schrieb über Entfernen von Daten aus einem Git-Repository .

Sagen wir, Ihr Git-Verlauf ist:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Beachten Sie, dass git lola ist ein nicht standardisierter, aber sehr nützlicher Alias. Mit dem --name-status Schalter können wir die Baumänderungen sehen, die mit jeder Übergabe verbunden sind.

In der Übergabe "Careless" (deren SHA1-Objektname ce36c98 ist) wird die Datei oops.iso ist der DVD-Rip, der versehentlich hinzugefügt und in der nächsten Übergabe, cb14efd, entfernt wurde. Mit der im oben erwähnten Blogbeitrag beschriebenen Technik lautet der auszuführende Befehl:

git filter-branch --prune-empty -d /dev/shm/scratch \
  --index-filter "git rm --cached -f --ignore-unmatch oops.iso" \
  --tag-name-filter cat -- --all

Optionen:

  • --prune-empty entfernt Commits, die leer werden ( d.h. ändern Sie den Baum nicht) als Ergebnis des Filtervorgangs. Im typischen Fall führt diese Option zu einem saubereren Verlauf.
  • -d benennt ein temporäres, noch nicht existierendes Verzeichnis, das für die Erstellung des gefilterten Verlaufs verwendet wird. Wenn Sie mit einer modernen Linux-Distribution arbeiten, ist die Angabe eines Baum in /dev/shm wird zu einer schnelleren Ausführung führen .
  • --index-filter ist das Hauptereignis und läuft bei jedem Schritt in der Historie gegen den Index. Sie möchten entfernen oops.iso wo immer er gefunden wird, aber er ist nicht in allen Übertragungen vorhanden. Der Befehl git rm --cached -f --ignore-unmatch oops.iso löscht den DVD-Rip, wenn er vorhanden ist, und schlägt ansonsten nicht fehl.
  • --tag-name-filter beschreibt, wie Tag-Namen umgeschrieben werden können. Ein Filter von cat ist die Identitätsoperation. Ihr Repository, wie das obige Beispiel, hat möglicherweise keine Tags, aber ich habe diese Option aus Gründen der Allgemeinheit hinzugefügt.
  • -- gibt das Ende der Optionen für git filter-branch
  • --all unter -- ist die Kurzform für alle Schiedsrichter. Ihr Repository, wie das obige Beispiel, hat vielleicht nur eine Referenz (master), aber ich habe diese Option aus Gründen der Allgemeinheit hinzugefügt.

Nach einigem Hin und Her ist die Geschichte jetzt:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
|
| * f772d66 (refs/original/refs/heads/master) Login page
| | A   login.html
| * cb14efd Remove DVD-rip
| | D   oops.iso
| * ce36c98 Careless
|/  A   oops.iso
|   A   other.html
|
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Beachten Sie, dass die neue "Careless"-Übertragung nur other.html und dass der Commit "Remove DVD-rip" nicht mehr auf dem Master-Zweig ist. Der Zweig mit der Bezeichnung refs/original/refs/heads/master enthält Ihre ursprünglichen Übertragungen, falls Sie einen Fehler gemacht haben. Um sie zu entfernen, folgen Sie den Schritten in "Checkliste für die Verkleinerung eines Repositorys".

$ git update-ref -d refs/original/refs/heads/master
$ git reflog expire --expire=now --all
$ git gc --prune=now

Eine einfachere Alternative ist das Klonen des Repositorys, um die unerwünschten Teile zu entfernen.

$ cd ~/src
$ mv repo repo.old
$ git clone file:///home/user/src/repo.old repo

Mit einer file:///... clone URL kopiert Objekte, anstatt nur Hardlinks zu erstellen.

Jetzt ist Ihre Geschichte:

$ git lola --name-status
* 8e0a11c (HEAD, master) Login page
| A     login.html
* e45ac59 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

Die SHA1-Objektnamen für die ersten beiden Übertragungen ("Index" und "Admin page") blieben unverändert, da der Filtervorgang diese Übertragungen nicht verändert hat. "Unachtsam" verloren oops.iso und "Login-Seite" haben ein neues Elternteil bekommen, so dass ihre SHA1s a fait ändern.

Interaktive Umbasierung

Mit einer Geschichte von:

$ git lola --name-status
* f772d66 (HEAD, master) Login page
| A     login.html
* cb14efd Remove DVD-rip
| D     oops.iso
* ce36c98 Careless
| A     oops.iso
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

die Sie entfernen möchten oops.iso aus "Sorglos", als hätten Sie es nie hinzugefügt, und dann ist "DVD-Rip entfernen" für Sie nutzlos. Unser Plan für eine interaktive Neuauflage ist daher, "Admin-Seite" beizubehalten, "Sorglos" zu bearbeiten und "DVD-Rip entfernen" zu verwerfen.

Laufen $ git rebase -i 5af4522 startet einen Editor mit dem folgenden Inhalt.

pick ce36c98 Careless
pick cb14efd Remove DVD-rip
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

Indem wir unseren Plan ausführen, ändern wir ihn zu

edit ce36c98 Careless
pick f772d66 Login page

# Rebase 5af4522..f772d66 onto 5af4522
# ...

Das heißt, wir löschen die Zeile mit "DVD-Rip entfernen" und ändern die Operation für "Careless" in edit statt pick .

Nach dem Beenden des Editors durch Speichern erscheint eine Eingabeaufforderung mit der folgenden Meldung.

Stopped at ce36c98... Careless
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

Wie uns die Meldung sagt, befinden wir uns auf der "Careless"-Übertragung, die wir bearbeiten wollen, also führen wir zwei Befehle aus.

$ git rm --cached oops.iso
$ git commit --amend -C HEAD
$ git rebase --continue

Im ersten Fall wird die betreffende Datei aus dem Index entfernt. Die zweite ändert oder ergänzt "Careless" so, dass es der aktualisierte Index ist und -C HEAD weist Git an, die alte Commit-Nachricht wieder zu verwenden. Zum Schluss, git rebase --continue fährt mit dem Rest des Umbasierungsvorgangs fort.

Dies gibt einen Überblick über die Geschichte:

$ git lola --name-status
* 93174be (HEAD, master) Login page
| A     login.html
* a570198 Careless
| A     other.html
* 5af4522 Admin page
| A     admin.html
* e738b63 Index
  A     index.html

das ist das, was Sie wollen.

5 Stimmen

Warum kann ich nicht pushen, wenn ich git filter-branch verwende, einige Refs konnten nicht nach 'git@bitbucket.org:product/myproject.git' gepusht werden. Um zu verhindern, dass Sie die Historie verlieren, wurden nicht-schnell weitergeleitete Aktualisierungen zurückgewiesen.

11 Stimmen

Fügen Sie die -f (oder --force ) zu Ihrer Option git push Befehl: "Normalerweise weigert sich der Befehl, eine entfernte Referenz zu aktualisieren, die kein Vorfahre der lokalen Referenz ist, mit der sie überschrieben wurde. Dieses Flag schaltet die Prüfung aus. Dies kann dazu führen, dass das entfernte Repository Commits verliert; verwenden Sie es mit Vorsicht."

6 Stimmen

Dies ist eine wunderbar gründliche Antwort, die die Verwendung von git-filter-branch erklärt, um unerwünschte große Dateien aus der Historie zu entfernen, aber es ist erwähnenswert, dass, seit Greg seine Antwort schrieb, der BFG Repo-Cleaner veröffentlicht wurde, der oft schneller und einfacher zu verwenden ist - siehe meine Antwort für Details.

320voto

Gary Gauh Punkte 4371

Warum nicht diesen einfachen, aber wirkungsvollen Befehl verwenden?

git filter-branch --tree-filter 'rm -f DVD-rip' HEAD

があります。 --tree-filter wird der angegebene Befehl nach jedem Auschecken des Projekts ausgeführt und die Ergebnisse werden dann erneut bestätigt. In diesem Fall entfernen Sie eine Datei namens DVD-rip aus jedem Snapshot, unabhängig davon, ob sie vorhanden ist oder nicht.

Wenn Sie wissen, welcher Commit die große Datei eingeführt hat (z.B. 35dsa2), können Sie HEAD durch 35dsa2..HEAD ersetzen, um zu vermeiden, dass Sie zu viel Historie umschreiben, und so divergierende Commits vermeiden, wenn Sie noch nicht gepusht haben. Dieser Kommentar von @alpha_989 scheint mir zu wichtig, um ihn hier auszulassen.

Voir dieser Link .

7 Stimmen

Viel besser als bfg. Ich war nicht in der Lage, eine Datei aus einem Git mit bfg zu bereinigen, aber dieser Befehl half

4 Stimmen

Das ist großartig. Nur ein Hinweis für andere, dass Sie dies pro Zweig tun müssen, wenn die große Datei in mehreren Zweigen ist.

1 Stimmen

Das hat bei mir bei einem lokalen Commit funktioniert, den ich nicht auf GitHub hochladen konnte. Und es schien einfacher zu sein als die anderen Lösungen.

147voto

Donat Punkte 3209

100 Mal schneller als git filter-branch und einfacher

Es gibt sehr gute Antworten in diesem Thread, aber mittlerweile sind viele davon veraltet. Verwendung von git-filter-branch wird nicht mehr empfohlen, da es schwierig zu benutzen und bei großen Repositories furchtbar langsam ist.

git-filter-repo ist viel schneller und einfacher in der Anwendung.

git-filter-repo ist ein Python-Skript, das auf github verfügbar ist: https://github.com/newren/git-filter-repo . Wenn es installiert ist, sieht es aus wie ein normaler Git-Befehl und kann aufgerufen werden durch git filter-repo .

Sie benötigen nur eine Datei: das Python3-Skript git-filter-repo. Kopieren Sie es in einen Pfad, der in der PATH-Variablen enthalten ist. Unter Windows müssen Sie eventuell die erste Zeile des Skripts ändern (siehe INSTALL.md). Sie müssen Python3 auf Ihrem System installiert haben, aber das ist keine große Sache.

Zuerst können Sie

git filter-repo --analyze

Dies hilft Ihnen bei der Entscheidung, was als nächstes zu tun ist.

Sie können Ihre DVD-Rip-Datei überall löschen:

git filter-repo --invert-paths --path-match DVD-rip

Filter-repo ist wirklich schnell. Eine Aufgabe, die auf meinem Computer mit filter-branch etwa 9 Stunden dauerte, wurde mit filter-repo in 4 Minuten erledigt. Mit filter-repo kann man noch viele andere schöne Dinge tun. Lesen Sie dazu die Dokumentation.

Warnung: Führen Sie dies mit einer Kopie Ihres Repositorys durch. Viele Aktionen von filter-repo können nicht rückgängig gemacht werden. filter-repo ändert die Commit-Hashes aller geänderten Commits (natürlich) und aller ihrer Nachkommen bis hin zu den letzten Commits!

2 Stimmen

Wie übermittle ich die angewandten Änderungen (in meinem lokalen Repository) an ein entferntes Repository? Oder ist dies nicht möglich, und ich sollte das geänderte Repository in ein neues klonen?

4 Stimmen

@diman82: Am besten wäre es, ein neues leeres Repository zu erstellen, das entfernte Repository aus deinem geklonten Repository darauf zu setzen und zu pushen. Das ist allen Antworten hier gemeinsam: Du wirst viele neue Commit-Hashes erhalten. Das ist unvermeidlich, da die Commit-Hashes für den Inhalt und die Historie einer Repository garantieren. Der alternative Weg ist gefährlich, man könnte einen Force Push machen und dann gc ausführen, um die Dateien loszuwerden. Aber tun Sie das nicht, wenn Sie nicht sehr gut getestet haben und sich aller Konsequenzen bewusst sind!

0 Stimmen

Ich habe bereits gepusht (mit --force Option), funktionierte gut (zu einem geklonten Repository, als Vorsichtsmaßnahme).

144voto

Sridhar Sarnobat Punkte 21937

(Die beste Antwort, die ich auf dieses Problem gefunden habe, ist: https://stackoverflow.com/a/42544963/714112 (hierher kopiert, da dieser Thread in den Google-Suchergebnissen weit oben erscheint, der andere aber nicht)

Ein rasend schneller Shell-Einzeiler

Dieses Shell-Skript zeigt alle Blob-Objekte im Repository an, sortiert vom kleinsten zum größten.

Für mein Beispiel-Repos lief es ungefähr so 100 Mal schneller als die anderen, die hier zu finden sind.
Auf meinem bewährten Athlon II X4-System bewältigt er die Linux-Kernel-Repository mit seinen 5.622.155 Objekten in etwas mehr als eine Minute .

Das Basisskript

git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| awk '/^blob/ {print substr($0,6)}' \
| sort --numeric-sort --key=2 \
| cut --complement --characters=13-40 \
| numfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest

Wenn Sie den obigen Code ausführen, erhalten Sie eine schöne menschenlesbare Ausgabe wie diese:

...
0d99bb931299  530KiB path/to/some-image.jpg
2ba44098e28f   12MiB path/to/hires-image.png
bd1741ddce0d   63MiB path/to/some-video-1080p.mp4

Schnelles Entfernen von Dateien

Angenommen, Sie wollen die Dateien entfernen a y b von jeder Übergabe, die von HEAD können Sie diesen Befehl verwenden:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD

6 Stimmen

Wenn Ihr Repository Tags enthält, möchten Sie wahrscheinlich auch das Flag --tag-name-filter cat um die entsprechenden neuen Commits neu zu markieren, wenn sie umgeschrieben werden, d.h., git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD (siehe diese verwandte Antwort )

3 Stimmen

Mac-Anweisungen und weitere Informationen finden Sie im verlinkten Originalbeitrag

3 Stimmen

git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEAD Arbeitsauftrag gleich zu Beginn

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X