Entfernen Sie den Ordner und seinen Inhalt aus dem Verlauf von git / GitHub

316

Ich habe an einem Repository in meinem GitHub-Konto gearbeitet und auf dieses Problem bin ich gestoßen.

  • Node.js-Projekt mit einem Ordner, in dem einige npm-Pakete installiert sind
  • Die Pakete befanden sich im node_modulesOrdner
  • Fügte diesen Ordner zum Git-Repository hinzu und schob den Code zu Github (dachte zu diesem Zeitpunkt nicht an den npm-Teil)
  • Es wurde erkannt, dass Sie diesen Ordner nicht wirklich benötigen, um Teil des Codes zu sein
  • Diesen Ordner gelöscht, verschoben

Zu diesem Zeitpunkt betrug die Größe des gesamten Git- Repos etwa 6 MB, wobei der tatsächliche Code (alle außer diesem Ordner) nur etwa 300 KB betrug .

Was ich am Ende suche, ist eine Möglichkeit, Details dieses Paketordners aus dem Git-Verlauf zu entfernen. Wenn jemand ihn klont, muss er keinen Verlauf im Wert von 6 MB herunterladen, in dem die einzigen tatsächlichen Dateien angezeigt werden Ab dem letzten Commit wären 300 KB.

Ich habe nach möglichen Lösungen dafür gesucht und diese beiden Methoden ausprobiert

Das Wesentliche schien zu funktionieren, als nach dem Ausführen des Skripts gezeigt wurde, dass dieser Ordner entfernt wurde, und danach, dass 50 verschiedene Commits geändert wurden. Aber ich konnte diesen Code nicht weitergeben. Als ich versuchte, es zu pushen, hieß es, Branch up to dateaber es wurden 50 Commits auf a geändert git status. Die anderen beiden Methoden haben auch nicht geholfen.

Obwohl sich herausstellte, dass der Verlauf dieses Ordners gelöscht wurde, waren es immer noch etwa 6 MB, als ich die Größe dieses Repos auf meinem lokalen Host überprüfte. (Ich habe auch den refs/originalOrdner gelöscht , aber die Änderung der Größe des Repos nicht gesehen).

Was ich klären möchte, ist, ob es eine Möglichkeit gibt, nicht nur den Commit-Verlauf (der das einzige ist, was meiner Meinung nach passiert ist) loszuwerden, sondern auch die Dateien, von denen git immer wieder annimmt, dass man einen Rollback durchführen möchte.

Nehmen wir an, es wird eine Lösung dafür vorgestellt, die auf meinen lokalen Host angewendet wird, aber nicht auf dieses GitHub-Repo reproduziert werden kann. Ist es möglich, dieses Repo zu klonen, auf das erste Commit zurückzusetzen, den Trick auszuführen und es zu pushen (oder bedeutet das, dass Git es tut? Haben Sie noch eine Geschichte all dieser Commits? - aka. 6MB).

Mein Endziel hier ist es, im Grunde den besten Weg zu finden, um den Ordnerinhalt von git zu entfernen, damit ein Benutzer keine Inhalte im Wert von 6 MB herunterladen muss und möglicherweise noch die anderen Commits hat, die den Modulordner nie berührt haben (das ist hübsch viel von ihnen) in der Geschichte von Git.

Wie kann ich das machen?

Kartik
quelle
3
Wenn eine der folgenden Antworten Ihr Problem gelöst hat, sollten Sie möglicherweise in Betracht ziehen, eine als Antwort auf Ihre Frage zu akzeptieren. meta.stackexchange.com/questions/5234/…
starbeamrainbowlabs
Die beste Antwort ist: stackoverflow.com/a/32886427/5973334
Kuzeko

Antworten:

556

Wenn Sie hier sind, um Code zu kopieren und einzufügen:

Dies ist ein Beispiel, das node_modulesaus der Geschichte entfernt wird

git filter-branch --tree-filter "rm -rf node_modules" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

Was Git eigentlich macht:

In der ersten Zeile werden alle Referenzen in demselben Baum ( --tree-filter) wie HEAD (Ihr aktueller Zweig) durchlaufen und der Befehl ausgeführt rm -rf node_modules. Dieser Befehl löscht die node_modules Ordner ( -rohne -r, rmwird nicht löschen Ordner), ohne Aufforderung an den Benutzer gegeben ( -f). Das Hinzufügen --prune-emptylöscht nutzlose (nichts ändernde) Commits rekursiv.

In der zweiten Zeile wird der Verweis auf diesen alten Zweig gelöscht.

Der Rest der Befehle ist relativ einfach.

Mohsen
quelle
3
Nur eine Randnotiz: Früher habe ich git count-objects -vüberprüft, ob die Dateien tatsächlich entfernt wurden, aber die Größe des Repositorys bleibt gleich, bis ich das Repository erneut geklont habe. Git enthält eine Kopie aller Originaldateien, die ich denke.
Davide Icardi
4
Mit einem nicht-alten git, sollte dies wahrscheinlich lesen --force-with-lease, nicht --force.
Griwes
4
Keiner dieser Befehle funktioniert unter Windows. Oder zumindest nicht Windows 10, bitte posten Sie das Betriebssystem, auf dem das "Ausschneiden und Einfügen" funktioniert
David
3
Für Windows 10-Benutzer funktioniert dies gut unter Bash für Windows (ich habe Ubuntu verwendet)
Andrej Kyselica
3
Ich habe es mit Windows Shell und mit Git Bash versucht und es hat nicht funktioniert. Erster Befehl bestanden, zweiter Befehl fehlgeschlagen!
Mohy Eldeen
238

Ich finde, dass die --tree-filterin anderen Antworten verwendete Option sehr langsam sein kann, insbesondere bei größeren Repositorys mit vielen Commits.

Hier ist die Methode, mit der ich ein Verzeichnis mit der --index-filterOption, die viel schneller ausgeführt wird , vollständig aus dem Git-Verlauf entfernen kann :

# Make a fresh clone of YOUR_REPO
git clone YOUR_REPO
cd YOUR_REPO

# Create tracking branches of all branches
for remote in `git branch -r | grep -v /HEAD`; do git checkout --track $remote ; done

# Remove DIRECTORY_NAME from all commits, then remove the refs to the old commits
# (repeat these two commands for as many directories that you want to remove)
git filter-branch --index-filter 'git rm -rf --cached --ignore-unmatch DIRECTORY_NAME/' --prune-empty --tag-name-filter cat -- --all
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d

# Ensure all old refs are fully removed
rm -Rf .git/logs .git/refs/original

# Perform a garbage collection to remove commits with no refs
git gc --prune=all --aggressive

# Force push all branches to overwrite their history
# (use with caution!)
git push origin --all --force
git push origin --tags --force

Sie können die Größe des Repositorys vor und nach dem gcmit überprüfen :

git count-objects -vH
Lee Netherton
quelle
3
Können Sie erklären, warum dies viel schneller ist?
Knocte
7
@knocte: aus den Dokumenten ( git-scm.com/docs/git-filter-branch ). "--index-filter: ... ähnelt dem Baumfilter, überprüft den Baum jedoch nicht, wodurch er viel schneller wird"
Lee Netherton,
23
Warum ist dies nicht die akzeptierte Antwort? Es ist so gründlich.
Mad Physicist
2
Wenn Sie dies in Windows tun, benötigen Sie doppelte Anführungszeichen anstelle von einfachen Anführungszeichen.
Kris Morness
12
Das Übergeben --quietdes git rmoben
Gesagten
46

Neben der beliebten Antwort oben Ich möchte ein paar Notizen für hinzufügen Windows - -Systeme. Der Befehl

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
  • funktioniert perfekt ohne Modifikation! Daher müssen Sie nicht verwenden Remove-Item, deloder irgendetwas anderes statt rm -rf.

  • Wenn Sie einen Pfad zu einer Datei oder einem Verzeichnis angeben müssen, verwenden Sie Schrägstriche wie./path/to/node_modules

Teilnehmer
quelle
Dies funktioniert unter Windows nicht, wenn das Verzeichnis a enthält. (Punkt) im Namen.
Corneliu Serediuc
4
Und ich habe die Lösung gefunden. Verwenden Sie doppelte Anführungszeichen für den Befehl rm wie folgt: "rm -rf node.modules".
Corneliu Serediuc
23

Die beste und genaueste Methode, die ich gefunden habe, war das Herunterladen der Datei bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/

Führen Sie dann die folgenden Befehle aus:

git clone --bare https://project/repository project-repository
cd project-repository
java -jar bfg.jar --delete-folders DIRECTORY_NAME  # i.e. 'node_modules' in other examples
git reflog expire --expire=now --all && git gc --prune=now --aggressive
git push --mirror https://project/new-repository

Wenn Sie Dateien löschen möchten, verwenden Sie stattdessen die Option Dateien löschen:

java -jar bfg.jar --delete-files *.pyc
Kim T.
quelle
1
Sehr einfach :) Wenn Sie sicherstellen möchten, dass nur ein bestimmter Ordner entfernt wird, hilft dies: stackoverflow.com/questions/21142986/…
emjay
7

Vervollständigen Sie das Rezept zum Kopieren und Einfügen, indem Sie einfach die Befehle in den Kommentaren hinzufügen (für die Lösung zum Kopieren und Einfügen), nachdem Sie sie getestet haben:

git filter-branch --tree-filter 'rm -rf node_modules' --prune-empty HEAD
echo node_modules/ >> .gitignore
git add .gitignore
git commit -m 'Removing node_modules from git history'
git gc
git push origin master --force

Danach können Sie die Zeile "node_modules /" aus .gitignore entfernen

jgbarah
quelle
Warum würden Sie dann entfernen node_modulesaus .gitignore? Damit sie versehentlich wieder begangen werden können ??
Adamski
1
Es wird nicht aus dem Gitignore entfernt, sondern dem Gitignore hinzugefügt. Die Commit-Nachricht sagt "Git-Geschichte", nicht "Gitignore" :)
Danny Tuppeny
aber der Kommentar sagt , dass Sie können dann entfernen node_modulesaus .gitignore.
Zavr
7

Beachten Sie für Windows-Benutzer, dass "anstelle von ' Auch hinzugefügt verwendet wird -f, um den Befehl zu erzwingen, wenn bereits eine andere Sicherung vorhanden ist.

git filter-branch -f --tree-filter "rm -rf FOLDERNAME" --prune-empty HEAD
git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
echo FOLDERNAME/ >> .gitignore
git add .gitignore
git commit -m "Removing FOLDERNAME from git history"
git gc
git push origin master --force
kcode
quelle
7

Es scheint , dass die up-to-date Antwort auf diese Frage ist nicht zu verwenden , filter-branchdirekt (mindestens git selbst es nicht mehr empfehlen) und defer , dass die Arbeit zu einem externen Tool. Insbesondere wird derzeit Git-Filter-Repo empfohlen. Der Autor dieses Tools liefert Argumente dafür, warum die filter-branchdirekte Verwendung zu Problemen führen kann.

Die meisten der oben diraus dem Verlauf zu entfernenden mehrzeiligen Skripte können wie folgt umgeschrieben werden:

git filter-repo --path dir --invert-paths

Das Tool ist anscheinend leistungsfähiger als nur das. Sie können Filter nach Autor, E-Mail, Referenzname und mehr anwenden ( vollständige Manpage hier ). Darüber hinaus ist es schnell . Die Installation ist einfach - sie wird in verschiedenen Formaten verteilt .

André Anjos
quelle
Schönes Werkzeug! Funktioniert gut unter Ubuntu 20.04, Sie können es nur, pip3 install git-filter-repoda es nur stdlib ist und keine Abhängigkeiten installiert. Unter Ubuntu 18 ist es nicht mit der Git-Version der Distribution kompatibel Error: need a version of git whose diff-tree command has the --combined-all-paths option, aber es ist einfach genug, um es auf einemdocker run -ti ubuntu:20.04
kubanczyk
3

Ich habe die Ordner bin und obj aus alten C # -Projekten mit git unter Windows entfernt. Sei vorsichtig mit

git filter-branch --tree-filter "rm -rf bin" --prune-empty HEAD

Es zerstört die Integrität der Git-Installation, indem der Ordner usr / bin im Git-Installationsordner gelöscht wird.

LordObi
quelle