Wie entferne / lösche ich eine große Datei aus dem Commit-Verlauf im Git-Repository?

708

Gelegentlich habe ich einen DVD-Rip in ein Website-Projekt gelegt, dann nachlässig git commit -a -m ..., und zap, das Repo wurde durch 2,2 Gigs aufgebläht. Das nächste Mal habe ich einige Änderungen vorgenommen, die Videodatei gelöscht und alles festgeschrieben, aber die komprimierte Datei befindet sich noch im Repository im Verlauf.

Ich weiß, dass ich aus diesen Commits Zweige starten und einen Zweig auf einen anderen umstellen kann. Aber was soll ich tun, um die beiden Commits zusammenzuführen, damit die große Datei nicht im Verlauf angezeigt wird und bei der Speicherbereinigung bereinigt wird?

culebrón
quelle
9
Dieser Artikel soll Ihnen helfen.github.com/removing-sensitive-data
MBO
1
Beachten Sie, dass Sie den vollständigen relativen Pfad angeben müssen, wenn sich Ihre große Datei in einem Unterverzeichnis befindet.
Johan
1
Auch verwandte help.github.com/de/articles/…
frederj
Viele Antworten unten ankündigen BFG als einfacher als git filter-branch, aber ich fand das Gegenteil wahr.
2540625

Antworten:

605

Verwenden Sie den BFG Repo-Cleaner , eine einfachere und schnellere Alternative zum git-filter-branchEntfernen unerwünschter Dateien aus dem Git-Verlauf.

Befolgen Sie sorgfältig die Gebrauchsanweisung , der Kern ist genau dies:

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

Alle Dateien mit einer Größe von mehr als 100 MB (die nicht in Ihrem letzten Commit enthalten sind) werden aus dem Verlauf Ihres Git-Repositorys entfernt. Sie können dann git gcdie toten Daten entfernen:

$ git gc --prune=now --aggressive

Das BFG ist in der Regel mindestens 10-50-mal schneller als das Laufen git-filter-branchund im Allgemeinen einfacher zu verwenden.

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

Roberto Tyley
quelle
4
@tony Es lohnt sich, den gesamten Klon- und Löschvorgang zu wiederholen, um festzustellen, ob die Meldung, die Sie zum Abrufen auffordert, erneut auftritt. Dies liegt jedoch mit ziemlicher Sicherheit daran, dass Ihr Remote-Server so konfiguriert ist, dass er Aktualisierungen ohne schnellen Vorlauf ablehnt (dh so konfiguriert ist, dass er Sie aufhält vor dem Verlust der Geschichte - genau das möchten Sie tun). Sie müssen diese Einstellung auf der Fernbedienung ändern. Andernfalls können Sie den aktualisierten Repo-Verlauf in ein brandneues leeres Repo verschieben.
Roberto Tyley
1
@ RobertoTyley Danke. Ich habe es 3 verschiedene Male versucht und alle haben die gleiche Nachricht erhalten. Daher denke ich auch, dass Sie Recht haben, wenn der Remote-Server so konfiguriert ist, dass er die Updates ohne schnellen Vorlauf ablehnt. Ich werde in Betracht ziehen, das aktualisierte Repo einfach auf ein brandneues Repo zu verschieben. Vielen Dank!
Tony
7
@ RobertoTyley Perfekt, du sparst meine Zeit, vielen Dank. Übrigens sollte vielleicht git push --forcenach Ihren Schritten tun , sonst wird das Remote-Repo immer noch nicht geändert.
22.
3
+1 zum Hinzufügen git push --force. Ebenfalls erwähnenswert: Force-Pushs werden von der Fernbedienung möglicherweise nicht zugelassen (gitlab.com standardmäßig nicht. Der Zweig musste "ungeschützt" werden).
MatrixManAtYrService
25
Ich denke, der Trump-Jargon, den das Tool ausgibt, ist ein bisschen viel.
Chris
564

Was Sie tun möchten, ist äußerst störend, wenn Sie den Verlauf für andere Entwickler veröffentlicht haben. Siehe „Wiederherstellen von Upstream Rebase“ in der git rebaseDokumentation für die notwendigen Schritte nach der Geschichte zu reparieren.

Sie haben mindestens zwei Optionen: git filter-branchund eine interaktive Rebase, die beide unten erläutert werden.

Verwenden von git filter-branch

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

Sagen Sie, Ihre Git-Geschichte 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 dies git lolaein nicht standardmäßiger, aber äußerst nützlicher Alias ​​ist. Mit dem --name-statusSchalter können wir Baumänderungen sehen, die mit jedem Commit verbunden sind.

Beim Commit "Careless" (dessen SHA1-Objektname ce36c98 lautet) ist die Datei oops.isoder DVD-Rip, der versehentlich hinzugefügt und beim nächsten Commit, cb14efd, entfernt wurde. Unter Verwendung der im oben genannten Blog-Beitrag 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-emptyEntfernt Commits, die aufgrund der Filteroperation leer werden ( dh den Baum nicht ändern). Im typischen Fall erzeugt diese Option einen saubereren Verlauf.
  • -dBenennt ein temporäres Verzeichnis, das noch nicht zum Erstellen des gefilterten Verlaufs verwendet werden kann. Wenn Sie auf einer modernen Linux-Distribution arbeiten, führt die Angabe eines Baums in /dev/shmzu einer schnelleren Ausführung .
  • --index-filterist das Hauptereignis und wird bei jedem Schritt im Verlauf gegen den Index ausgeführt. Sie möchten entfernen, oops.isowo immer es gefunden wird, aber es ist nicht in allen Commits vorhanden. Der Befehl git rm --cached -f --ignore-unmatch oops.isolöscht den DVD-Rip, wenn er vorhanden ist, und schlägt ansonsten nicht fehl.
  • --tag-name-filterbeschreibt, wie Tag-Namen umgeschrieben werden. Ein Filter von catist die Identitätsoperation. Ihr Repository hat wie das obige Beispiel möglicherweise keine Tags, aber ich habe diese Option aus Gründen der allgemeinen Allgemeinheit eingefügt.
  • -- Gibt das Ende der Optionen an git filter-branch
  • --allDas Folgende --ist eine Abkürzung für alle Refs. Ihr Repository hat wie das obige Beispiel möglicherweise nur eine Referenz (Master), aber ich habe diese Option aus Gründen der allgemeinen Allgemeinheit aufgenommen.

Nach einigem Hin und Her ist die Geschichte nun:

$ 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 das neue Commit "Unvorsichtig" nur other.htmlhinzugefügt wird und dass sich das Commit "DVD-Rip entfernen" nicht mehr im Hauptzweig befindet. Der gekennzeichnete Zweig refs/original/refs/heads/masterenthält Ihre ursprünglichen Commits, falls Sie einen Fehler gemacht haben. Befolgen Sie zum Entfernen die Schritte unter „Checkliste zum Verkleinern eines Repositorys“.

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

Für eine einfachere Alternative klonen Sie das Repository, um die unerwünschten Bits zu verwerfen.

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

Durch die Verwendung einer file:///...Klon-URL werden Objekte kopiert, 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 Commits ("Index" und "Admin-Seite") blieben unverändert, da die Filteroperation diese Commits nicht geändert hat. „Careless“ verloren oops.isound „Login - Seite“ haben ein neu Eltern, so dass ihre SHA1s tat ändern.

Interaktive Rebase

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

Sie möchten oops.isoaus "Unvorsichtig" entfernen, als hätten Sie es nie hinzugefügt, und dann ist "DVD-Rip entfernen" für Sie nutzlos. Daher ist unser Plan für eine interaktive Rebase, die "Admin-Seite" beizubehalten, "Nachlässig" zu bearbeiten und "DVD-Rip entfernen" zu verwerfen.

Durch Ausführen wird $ git rebase -i 5af4522ein Editor mit den folgenden Inhalten gestartet.

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.
#

Wir führen unseren Plan aus und ändern ihn in

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 den Vorgang auf "Unvorsichtig" so, dass er editnicht pick.

Wenn Sie das Speichern des Editors beenden, wird uns an einer Eingabeaufforderung die folgende Meldung angezeigt.

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 aus der Nachricht hervorgeht, befinden wir uns im Commit "Unvorsichtig", das wir bearbeiten möchten, und führen daher zwei Befehle aus.

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

Der erste entfernt die fehlerhafte Datei aus dem Index. Der zweite ändert oder ändert "Careless" als aktualisierten Index und -C HEADweist git an, die alte Festschreibungsnachricht wiederzuverwenden. Schließlich wird git rebase --continuemit dem Rest der Rebase-Operation fortgefahren.

Dies gibt eine Geschichte von:

$ 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

Welches ist, was Sie wollen.

Greg Bacon
quelle
4
Warum ich bei Verwendung von git filter-branch nicht pushen kann, konnte einige Verweise nicht an '[email protected]: product / myproject.git' senden. Um zu verhindern, dass Sie den Verlauf verlieren, wurden Aktualisierungen ohne schnellen Vorlauf abgelehnt. Führen Sie die Fernbedienung zusammen Änderungen vor dem erneuten Drücken.
Agung Prasetyo
11
Fügen Sie Ihrem Befehl die Option -f(oder --force) hinzu git push: „Normalerweise weigert sich der Befehl, eine Remote-Referenz zu aktualisieren, die kein Vorfahr der lokalen Referenz ist, die zum Überschreiben verwendet wurde. Dieses Flag deaktiviert die Prüfung. Dies kann dazu führen, dass das Remote-Repository Commits verliert. benutze es mit Vorsicht. "
Greg Bacon
5
Dies ist eine wunderbar gründliche Antwort, die die Verwendung von Git-Filter-Branch zum Entfernen unerwünschter großer Dateien aus dem Verlauf erklärt. Es ist jedoch erwähnenswert, dass seit Greg seine Antwort geschrieben hat, The BFG Repo-Cleaner veröffentlicht wurde, was oft schneller und einfacher ist Verwenden Sie - siehe meine Antwort für Details.
Roberto Tyley
1
Nachdem ich eine der oben genannten Prozeduren ausgeführt habe, löscht das Remote-Repository (auf GitHub) die große Datei NICHT. Nur der Einheimische tut es. Ich zwinge Push und Nada. Was vermisse ich?
Azatar
1
Dies funktioniert auch bei Dirs. ... "git rm --cached -rf --ignore-unmatch path/to/dir"...
Rynop
198

Warum nicht diesen einfachen, aber leistungsstarken Befehl verwenden?

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

Die --tree-filterOption führt den angegebenen Befehl nach jedem Auschecken des Projekts aus und schreibt die Ergebnisse erneut fest. 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, durch welches Commit die große Datei eingeführt wurde (z. B. 35dsa2), können Sie HEAD durch 35dsa2..HEAD ersetzen, um zu vermeiden, dass zu viel Verlauf neu geschrieben wird, und um divergierende Commits zu vermeiden, wenn Sie noch kein Push durchgeführt haben. Dieser Kommentar mit freundlicher Genehmigung von @ alpha_989 scheint zu wichtig, um ihn hier wegzulassen.

Siehe diesen Link .

Gary Gauh
quelle
3
Das ist eine gute Lösung! Ich habe einen Kern geschaffen , das ein Python - Skript , um die Dateien aufzulisten hat und die git cmd, der die Datei löschen , werden Sie reinigen wollen gist.github.com/ariv3ra/16fd94e46345e62cfcbf
punkdata
5
Viel besser als bfg. Ich war nicht in der Lage, Datei von einem Git mit bfg zu bereinigen, aber dieser Befehl half
podarok
4
Das ist toll. Nur ein Hinweis für andere, dass Sie dies pro Zweig tun müssen, wenn sich die große Datei in mehreren Zweigen befindet.
James
2
Unter Windows habe ich bekommen fatal: bad revision 'rm', was ich mit "statt behoben habe '. Gesamtbefehl:git filter-branch --force --index-filter "git rm --cached -r --ignore-unmatch oops.iso" --prune-empty --tag-name-filter cat -- --all
Marcotama
2
Wenn Sie das wissen , commitwo Sie die Datei in (sagen wir setzen 35dsa2) können Sie ersetzen HEADmit 35dsa2..HEAD. tree-filterist viel langsamer als auf index-filterdiese Weise. Es wird nicht versucht, alle Commits auszuchecken und neu zu schreiben. Wenn Sie HEAD verwenden, wird dies versucht.
Alpha_989
86

(Die beste Antwort, die ich auf dieses Problem gesehen habe, ist: https://stackoverflow.com/a/42544963/714112 , hier kopiert, da dieser Thread in den Google-Suchrankings hoch erscheint, der andere jedoch nicht.)

🚀 Ein blitzschneller Shell-Einzeiler 🚀

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

Für mein Beispiel-Repo lief es ungefähr 100-mal schneller als die anderen hier gefundenen.
Auf meinem vertrauenswürdigen Athlon II X4-System verwaltet es das Linux-Kernel-Repository mit seinen 5.622.155 Objekten in etwas mehr als einer 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, für Menschen lesbare Ausgabe wie folgt :

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

🚀 Schnelle Dateientfernung 🚀

Angenommen, Sie möchten dann die Dateien entfernen aund bvon jedem Commit, von dem aus HEADSie erreichbar sind , können Sie diesen Befehl verwenden:

git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' HEAD
Sridhar Sarnobat
quelle
3
Wenn Ihr Repo Tags hat, möchten Sie wahrscheinlich auch das Flag hinzufügen, um die --tag-name-filter catneuen entsprechenden Commits neu zu markieren, wenn sie neu geschrieben werden, dh git filter-branch --index-filter 'git rm --cached --ignore-unmatch a b' --tag-name-filter cat HEAD(siehe diese verwandte Antwort )
naitsirhc
3
Mac-Anweisungen und einige andere Informationen erscheinen in der ursprünglich verlinkten Post
Nr.
3
git filter-branch --index-filter 'git rm --cached --ignore-unmatch <filename>' HEADArbeitsauftrag rechts von der Fledermaus
eleijonmarck
meine Lieblingsantwort. eine leichte Optimierung für Mac OS (mit Gnu-Befehlen)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 \ | gnumfmt --field=2 --to=iec-i --suffix=B --padding=7 --round=nearest
Florian Oswald
cooles Skript mit der Rev-Liste, aber es hat bei mir als Alias ​​nicht funktioniert, eine Idee, wie das geht?
Robin Manoli
47

Nachdem ich praktisch jede Antwort in SO ausprobiert hatte, fand ich endlich dieses Juwel, das die großen Dateien in meinem Repository schnell entfernte und löschte und es mir ermöglichte, erneut zu synchronisieren: http://www.zyxware.com/articles/4027/how-to-delete -Dateien-permanent-von-Ihren-lokalen-und-Remote-Git-Repositorys

CD in Ihren lokalen Arbeitsordner und führen Sie den folgenden Befehl aus:

git filter-branch -f --index-filter "git rm -rf --cached --ignore-unmatch FOLDERNAME" -- --all

Ersetzen Sie FOLDERNAME durch die Datei oder den Ordner, den Sie aus dem angegebenen Git-Repository entfernen möchten.

Führen Sie anschließend die folgenden Befehle aus, um das lokale Repository zu bereinigen:

rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

Übertragen Sie nun alle Änderungen in das Remote-Repository:

git push --all --force

Dadurch wird das Remote-Repository bereinigt.

Justin
quelle
Arbeitete wie ein Zauber für mich.
Ramon Vasconcelos
3
Das hat auch bei mir funktioniert. Entfernt einen bestimmten Ordner (in meinem Fall einen, der zu große Dateien oder ein Github-Repo enthielt) im Repository, behält ihn jedoch im lokalen Dateisystem bei, falls vorhanden.
Skizzo
Hat für mich gearbeitet! Es ist kein Verlauf mehr vorhanden, der möglicherweise verwirrend ist (wenn jemand gerade klonen soll), stellen Sie sicher, dass Sie einen Plan haben, um defekte Links, Abhängigkeiten usw. zu aktualisieren
ruoho ruotsi
38

Diese Befehle funktionierten in meinem Fall:

git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now
git gc --aggressive --prune=now

Es unterscheidet sich kaum von den oben genannten Versionen.

Für diejenigen, die dies auf Github / Bitbucket schieben müssen (ich habe dies nur mit Bitbucket getestet):

# WARNING!!!
# this will rewrite completely your bitbucket refs
# will delete all branches that you didn't have in your local

git push --all --prune --force

# Once you pushed, all your teammates need to clone repository again
# git pull will not work
Kostanos
quelle
4
Wie unterscheidet es sich von oben, warum ist es besser?
Andy Hayden
1
Aus irgendeinem Grund ist die mkljun-Version in meinem Fall nicht reduziert. Ich hatte die Dateien bereits mithilfe von aus dem Index entfernt git rm --cached files. Der Vorschlag von Greg Bacon ist vollständiger und entspricht dem meiner Mine, aber er hat den --force-Index für Fälle verpasst, in denen Sie Filter-Branch mehrmals verwenden, und er hat so viele Informationen geschrieben, dass meine Version wie ein Lebenslauf ist davon.
Kostanos
1
Dies hat wirklich geholfen, aber ich musste die -fOption nicht nur -rfhier verwenden, git rm --cached -rf --ignore-unmatch oops.isosondern git rm --cached -r --ignore-unmatch oops.isogemäß @ lfender6445 unten
drstevok
10

Beachten Sie nur, dass diese Befehle sehr zerstörerisch sein können. Wenn mehr Leute am Repo arbeiten, müssen sie alle den neuen Baum ziehen. Die drei mittleren Befehle sind nicht erforderlich, wenn Sie die Größe NICHT reduzieren möchten. Weil der Filterzweig eine Sicherungskopie der entfernten Datei erstellt und dort lange bleiben kann.

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force
mkljun
quelle
11
Führen Sie diese Befehle NICHT aus, es sei denn, Sie möchten sich selbst immense Schmerzen zufügen. Es hat viele meiner ursprünglichen Quellcodedateien gelöscht. Ich ging davon aus, dass einige große Dateien aus meinem Commit-Verlauf in GIT gelöscht werden (gemäß der ursprünglichen Frage). Ich denke jedoch, dass dieser Befehl dazu dient, Dateien dauerhaft aus Ihrem ursprünglichen Quellcodebaum zu löschen (großer Unterschied!). Mein System: Windows, VS2012, Git Source Control Provider.
Contango
2
Ich habe diesen Befehl verwendet: git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch oops.iso' --prune-empty --tag-name-filter cat -- --allanstelle des ersten aus Ihrem Code
Kostanos
8

Wenn Sie wissen, dass Ihr Commit kürzlich ausgeführt wurde, anstatt den gesamten Baum zu durchlaufen, gehen Sie wie folgt vor: git filter-branch --tree-filter 'rm LARGE_FILE.zip' HEAD~10..HEAD

Soheil
quelle
7

Ich bin darauf mit einem Bitbucket-Konto gestoßen, in dem ich versehentlich gigantische * .jpa-Backups meiner Site gespeichert hatte.

git filter-branch --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch MY-BIG-DIRECTORY-OR-FILE' --tag-name-filter cat -- --all

Platzieren Sie MY-BIG-DIRECTORYden betreffenden Ordner neu, um Ihren Verlauf ( einschließlich Tags ) vollständig neu zu schreiben .

Quelle: https://web.archive.org/web/20170727144429/http://naleid.com:80/blog/2012/01/17/finding-and-purging-big-files-from-git-history/

lfender6445
quelle
1
Diese Antwort hat mir geholfen, außer dass das Skript in der Antwort ein kleines Problem aufweist und nicht in allen Zweigen von mir gesucht wird. Aber der Befehl im Link hat es perfekt gemacht.
Ali B
5

Dadurch wird es aus Ihrem Verlauf entfernt

git filter-branch --force --index-filter 'git rm -r --cached --ignore-unmatch bigfile.txt' --prune-empty --tag-name-filter cat -- --all
funkeln
quelle
Das hat bei mir funktioniert danke !!
Sonja Brits
Das funktioniert in meinem Fall. Ich führe dies auf Ihrem Hauptzweig aus.
S. Domeng
4

Ich habe im Grunde genommen getan, was auf dieser Antwort stand: https://stackoverflow.com/a/11032521/1286423

(Für die Geschichte werde ich es hier kopieren und einfügen)

$ git filter-branch --index-filter "git rm -rf --cached --ignore-unmatch YOURFILENAME" HEAD
$ rm -rf .git/refs/original/ 
$ git reflog expire --all 
$ git gc --aggressive --prune
$ git push origin master --force

Es hat nicht funktioniert, weil ich Dinge gerne umbenenne und viel bewege. Einige große Dateien befanden sich also in Ordnern, die umbenannt wurden, und ich denke, der GC konnte den Verweis auf diese Dateien aufgrund des Verweises in treeObjekten, die auf diese Datei verweisen, nicht löschen . Meine ultimative Lösung, um es wirklich zu töten, war:

# First, apply what's in the answer linked in the front
# and before doing the gc --prune --aggressive, do:

# Go back at the origin of the repository
git checkout -b newinit <sha1 of first commit>
# Create a parallel initial commit
git commit --amend
# go back on the master branch that has big file
# still referenced in history, even though 
# we thought we removed them.
git checkout master
# rebase on the newinit created earlier. By reapply patches,
# it will really forget about the references to hidden big files.
git rebase newinit

# Do the previous part (checkout + rebase) for each branch
# still connected to the original initial commit, 
# so we remove all the references.

# Remove the .git/logs folder, also containing references
# to commits that could make git gc not remove them.
rm -rf .git/logs/

# Then you can do a garbage collection,
# and the hidden files really will get gc'ed
git gc --prune --aggressive

Mein Repo (das .git) hat sich von 32 MB auf 388 KB geändert, das selbst der Filterzweig nicht reinigen konnte.

Dolanor
quelle
4

git filter-branchist ein leistungsstarker Befehl, mit dem Sie eine große Datei aus dem Commit-Verlauf löschen können. Die Datei bleibt eine Weile und Git entfernt sie in der nächsten Garbage Collection. Im Folgenden finden Sie den vollständigen Vorgang zum Löschen von Dateien aus dem Festschreibungsverlauf . Aus Sicherheitsgründen führt der folgende Prozess zuerst die Befehle in einem neuen Zweig aus. Wenn das Ergebnis Ihren Anforderungen entspricht, setzen Sie es auf den Zweig zurück, den Sie tatsächlich ändern möchten.

# Do it in a new testing branch
$ git checkout -b test

# Remove file-name from every commit on the new branch
# --index-filter, rewrite index without checking out
# --cached, remove it from index but not include working tree
# --ignore-unmatch, ignore if files to be removed are absent in a commit
# HEAD, execute the specified command for each commit reached from HEAD by parent link
$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch file-name' HEAD

# The output is OK, reset it to the prior branch master
$ git checkout master
$ git reset --soft test

# Remove test branch
$ git branch -d test

# Push it with force
$ git push --force origin master
zhangyu12
quelle
2

Verwenden Sie Git Extensions , es ist ein UI-Tool. Es hat ein Plugin namens "Find large files", das große Dateien in Repositorys findet und das dauerhafte Entfernen ermöglicht.

Verwenden Sie 'git filter-branch' nicht, bevor Sie dieses Tool verwenden, da es keine Dateien finden kann, die von 'filter-branch' entfernt wurden (obwohl 'filter-branch' Dateien nicht vollständig aus den Repository-Pack-Dateien entfernt) .

Nir
quelle
Diese Methode ist für große Repositorys zu langsam. Das Auflisten der großen Dateien dauerte über eine Stunde. Wenn ich dann zum Löschen von Dateien gehe, ist es nach einer Stunde nur 1/3 der Verarbeitung der ersten Datei, die ich löschen möchte.
Kristianp
Ja, es ist langsam, aber funktioniert die Arbeit ... Wissen Sie etwas schneller?
Nir
1
Ich habe es nicht benutzt, aber BFG Repo-Cleaner, wie eine andere Antwort auf dieser Seite zeigt.
Kristianp
2

Sie können dies mit dem folgenden branch filterBefehl tun :

git filter-branch --tree-filter 'rm -rf path/to/your/file' HEAD

John Foley
quelle
2

Es gibt sehr gute Antworten in diesem Thread, aber mittlerweile sind viele davon veraltet. Die Verwendung git-filter-branchwird nicht mehr empfohlen, da die Verwendung in großen Repositorys schwierig und äußerst langsam ist.

git-filter-repo ist viel schneller und einfacher zu bedienen.

git-filter-repoist ein Python-Skript, das unter github verfügbar ist: https://github.com/newren/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 möglicherweise 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 kannst du rennen

git filter-repo --analyze

Auf diese Weise können Sie bestimmen, 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 sehr schnell. Eine Aufgabe, die auf meinem Computer mit Filter-Branch ungefähr 9 Stunden dauerte, wurde mit Filter-Repo in 4 Minuten erledigt. Mit Filter-Repo können Sie noch viele weitere schöne Dinge tun. Siehe dazu die Dokumentation.

Warnung: Führen Sie dies auf einer Kopie Ihres Repositorys aus. 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 zu den letzten Commits!

Donat
quelle
1

Wenn Sie auf dieses Problem stoßen, git rmwird dies nicht ausreichen, da git sich daran erinnert, dass die Datei einmal in unserem Verlauf vorhanden war, und daher einen Verweis darauf behält.

Um die Sache noch schlimmer zu machen, ist das erneute Basieren auch nicht einfach, da jegliche Verweise auf den Blob verhindern, dass der Git-Garbage-Collector den Raum aufräumt. Dies umfasst Remote-Referenzen und Reflog-Referenzen.

Ich habe git forget-blobein kleines Skript zusammengestellt , das versucht, alle diese Referenzen zu entfernen, und dann git filter-branch verwendet, um jedes Commit in der Verzweigung neu zu schreiben.

Sobald Ihr Blob vollständig nicht mehr referenziert ist, git gcwird er entfernt

Die Verwendung ist ziemlich einfach git forget-blob file-to-forget. Weitere Informationen erhalten Sie hier

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-repository-with-git-forget-blob/

Ich habe dies dank der Antworten von Stack Overflow und einigen Blogeinträgen zusammengestellt. Credits an sie!

Nachoparker
quelle
Sie sollten dies in Homebrew
Cameron E
0

Neben git filter-branch(langsame, aber reine Git-Lösung) und BFG (einfacher und sehr leistungsfähiger) gibt es noch ein anderes Tool zum Filtern mit guter Leistung:

https://github.com/xoofx/git-rocket-filter

Aus seiner Beschreibung:

Der Zweck von Git-Rocket-Filter ähnelt dem Befehl git-filter-branchund bietet die folgenden einzigartigen Funktionen:

  • Schnelles Umschreiben von Commits und Bäumen (in der Größenordnung von x10 bis x100).
  • Integrierte Unterstützung für White-Listing mit --keep (behält Dateien oder Verzeichnisse) und Blacklisting mit --remove-Optionen.
  • Verwendung eines .gitignore-ähnlichen Musters für die Baumfilterung
  • Schnelles und einfaches C # -Skripting für die Festschreibungsfilterung und die Baumfilterung
  • Unterstützung für Skripte bei der Baumfilterung nach Datei- / Verzeichnismuster
  • Leeres / unverändertes Commit, einschließlich Merge-Commits, automatisch bereinigen
Philippe
quelle