Einführung: Sie haben 5 Lösungen zur Verfügung
Das Originalplakat besagt:
Ich habe versehentlich eine unerwünschte Datei festgeschrieben ... vor einigen Festschreibungen in mein Repository ... Ich möchte die Datei vollständig aus dem Repository-Verlauf löschen.
Ist es möglich, den Änderungsverlauf so umzuschreiben, dass er überhaupt filename.orig
nicht zum Repository hinzugefügt wurde?
Es gibt viele verschiedene Möglichkeiten, den Verlauf einer Datei vollständig aus git zu entfernen:
- Commits ändern.
- Hard Resets (möglicherweise plus Rebase).
- Nicht interaktive Rebase.
- Interaktive Rebases.
- Filtern von Zweigen.
Im Fall des Originalplakats ist die Änderung des Commits für sich genommen keine Option, da er anschließend mehrere zusätzliche Commits vorgenommen hat. Der Vollständigkeit halber werde ich jedoch auch erklären, wie dies für alle anderen zu tun ist, die es nur wollen ihre vorherige Verpflichtung zu ändern.
Beachten Sie, dass bei all diesen Lösungen der Verlauf / die Commits auf eine andere Weise geändert / neu geschrieben werden müssen . Daher muss jeder Benutzer mit alten Kopien der Commits zusätzliche Arbeit leisten, um seinen Verlauf mit dem neuen Verlauf neu zu synchronisieren.
Lösung 1: Ändern von Commits
Wenn Sie in Ihrem vorherigen Commit versehentlich eine Änderung vorgenommen haben (z. B. das Hinzufügen einer Datei) und der Verlauf dieser Änderung nicht mehr vorhanden sein soll, können Sie das vorherige Commit einfach ändern, um die Datei daraus zu entfernen:
git rm <file>
git commit --amend --no-edit
Lösung 2: Hard Reset (möglicherweise plus Rebase)
Wie bei Lösung Nr. 1 haben Sie auch die Möglichkeit, einfach einen Hard-Reset für das übergeordnete Element durchzuführen, wenn Sie nur Ihr vorheriges Commit entfernen möchten:
git reset --hard HEAD^
Dieser Befehl wird schwer zurückzusetzen Zweig zum vorherigen 1 st Eltern begehen.
Allerdings mit einem Fütterungsmaterial, wenn, wie das ursprüngliche Plakat, haben Sie mehrere Commits gemacht nach dem Commit Sie die Änderung rückgängig machen möchten, können Sie immer noch schwer Resets verwendenihn zu ändern, sondern beinhaltet auch so tun. Hier sind die Schritte, mit denen Sie ein Commit weiter hinten in der Geschichte ändern können:
# Create a new branch at the commit you want to amend
git checkout -b temp <commit>
# Amend the commit
git rm <file>
git commit --amend --no-edit
# Rebase your previous branch onto this new commit, starting from the old-commit
git rebase --preserve-merges --onto temp <old-commit> master
# Verify your changes
git diff master@{1}
Lösung 3: Nicht interaktive Wiederherstellung
Dies funktioniert, wenn Sie nur ein Commit vollständig aus dem Verlauf entfernen möchten:
# Create a new branch at the parent-commit of the commit that you want to remove
git branch temp <parent-commit>
# Rebase onto the parent-commit, starting from the commit-to-remove
git rebase --preserve-merges --onto temp <commit-to-remove> master
# Or use `-p` insteda of the longer `--preserve-merges`
git rebase -p --onto temp <commit-to-remove> master
# Verify your changes
git diff master@{1}
Lösung 4: Interaktive Rebases
Mit dieser Lösung können Sie die gleichen Aufgaben wie mit den Lösungen Nr. 2 und Nr. 3 ausführen, dh Commits weiter hinten im Verlauf ändern oder entfernen als mit Ihrem unmittelbar vorherigen Commit. Welche Lösung Sie verwenden, liegt also ganz bei Ihnen. Interaktive Rebases sind aus Leistungsgründen nicht gut geeignet, um Hunderte von Commits neu zu gründen. Daher würde ich in solchen Situationen nicht interaktive Rebases oder die Filterzweiglösung (siehe unten) verwenden.
Verwenden Sie Folgendes, um die interaktive Rebase zu starten:
git rebase --interactive <commit-to-amend-or-remove>~
# Or `-i` instead of the longer `--interactive`
git rebase -i <commit-to-amend-or-remove>~
Dies führt dazu, dass git den Festschreibungsverlauf auf das übergeordnete Element des Festschreibens zurückspult, das Sie ändern oder entfernen möchten. Anschließend wird eine Liste der zurückgespulten Commits in umgekehrter Reihenfolge in dem Editor angezeigt, für den git verwendet wird (dies ist standardmäßig Vim):
pick 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
pick 7668f34 Modify Bash config to use Homebrew recommended PATH
pick 475593a Add global .gitignore file for OS X
pick 1b7f496 Add alias for Dr Java to Bash config (OS X)
Das Commit, das Sie ändern oder entfernen möchten, befindet sich oben in dieser Liste. Um es zu entfernen, löschen Sie einfach die Zeile in der Liste. Andernfalls ersetzen „Pick“ auf „Bearbeiten“ auf der 1 st Linie, etwa so:
edit 00ddaac Add symlinks for executables
pick 03fa071 Set `push.default` to `simple`
Als nächstes geben Sie ein git rebase --continue
. Wenn Sie das Commit vollständig entfernen möchten, ist dies alles, was Sie tun müssen (außer der Überprüfung, siehe letzter Schritt für diese Lösung). Wenn Sie andererseits das Commit ändern möchten, wendet git das Commit erneut an und pausiert dann die Rebase.
Stopped at 00ddaacab0a85d9989217dd9fe9e1b317ed069ac... Add symlinks
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
Zu diesem Zeitpunkt können Sie die Datei entfernen, das Commit ändern und dann die Rebase fortsetzen:
git rm <file>
git commit --amend --no-edit
git rebase --continue
Das ist es. Als letzten Schritt sollten Sie unabhängig davon, ob Sie das Commit geändert oder vollständig entfernt haben, immer überprüfen, ob keine anderen unerwarteten Änderungen an Ihrem Zweig vorgenommen wurden, indem Sie den Status vor dem Rebase ändern:
git diff master@{1}
Lösung 5: Filtern von Zweigen
Schließlich ist diese Lösung am besten geeignet, wenn Sie alle Spuren der Existenz einer Datei vollständig aus dem Verlauf entfernen möchten und keine der anderen Lösungen der Aufgabe gerecht wird.
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>'
Dadurch werden <file>
alle Commits entfernt, beginnend mit dem Root-Commit. Wenn Sie stattdessen nur den Festschreibungsbereich neu schreiben möchten, HEAD~5..HEAD
können Sie dies als zusätzliches Argument an übergeben filter-branch
, wie in
dieser Antwort ausgeführt :
git filter-branch --index-filter \
'git rm --cached --ignore-unmatch <file>' HEAD~5..HEAD
Nach Abschluss des filter-branch
Vorgangs ist es normalerweise eine gute Idee, zu überprüfen, ob keine weiteren unerwarteten Änderungen vorliegen, indem Sie Ihren Zweig vor dem Filtervorgang vom vorherigen Status unterscheiden:
git diff master@{1}
Filter-Branch-Alternative: BFG Repo Cleaner
Ich habe gehört, dass das BFG Repo Cleaner- Tool schneller als ausgeführt git filter-branch
wird. Vielleicht möchten Sie dies auch als Option überprüfen. Es wird sogar offiziell in der Filterzweigdokumentation als praktikable Alternative erwähnt:
Mit Git-Filter-Branch können Sie komplexe Git-Skript-Umschreibungen Ihres Git-Verlaufs vornehmen. Diese Flexibilität benötigen Sie jedoch wahrscheinlich nicht, wenn Sie einfach unerwünschte Daten wie große Dateien oder Kennwörter entfernen . Für diese Vorgänge sollten Sie The BFG Repo-Cleaner in Betracht ziehen , eine JVM-basierte Alternative zum Git-Filter-Zweig, die für diese Anwendungsfälle in der Regel mindestens 10-50-mal schneller ist und ganz andere Eigenschaften aufweist:
Jede bestimmte Version einer Datei wird genau einmal bereinigt . Die BFG bietet Ihnen im Gegensatz zum Git-Filter-Zweig nicht die Möglichkeit, eine Datei anders zu behandeln, je nachdem, wo oder wann sie in Ihrem Verlauf festgeschrieben wurde. Diese Einschränkung bietet den entscheidenden Leistungsvorteil von The BFG und eignet sich gut für die Bereinigung fehlerhafter Daten. Es ist Ihnen egal, wo sich die fehlerhaften Daten befinden, Sie möchten nur, dass sie nicht mehr vorhanden sind .
Standardmäßig nutzt die BFG Multi-Core-Maschinen voll aus und bereinigt parallel Commit-Dateibäume. GIT-Filterzweig cleans Festschreibungen sequentiell (dh in einer Single-Threaded - Weise), obwohl es ist
möglich , Filter zu schreiben, die ihren eigenen Parallelismus umfassen, in den Skripten gegen jede ausgeführte begehen.
Die Befehlsoptionen sind viel restriktiver als der Git-Filter-Zweig und dienen ausschließlich der Aufgabe, unerwünschte Daten zu entfernen, z --strip-blobs-bigger-than 1M
.
Zusätzliche Ressourcen
- Pro Git § 6.4 Git Tools - Umschreiben des Verlaufs .
- Git-Filter-Zweig (1) Handbuch Seite .
- git-commit (1) Handbuchseite .
- git-reset (1) Handbuchseite .
- git-rebase (1) Handbuchseite .
- Der BFG Repo Cleaner (siehe auch diese Antwort des Erstellers selbst ).