Einrichtung und Konfiguration
Projekte holen und erstellen
Grundlegende Snapshots
Branching und Merging
Projekte teilen und aktualisieren
Inspektion und Vergleich
Patching
Debugging
Externe Systeme
Server-Administration
Anleitungen
- gitattributes
- Konventionen der Kommandozeile
- Tägliches Git
- Häufig gestellte Fragen (FAQ)
- Glossar
- Hooks
- gitignore
- gitmodules
- Revisionen
- Submodule
- Tutorial
- Workflows
- Alle Anleitungen...
Administration
Plumbing-Befehle
- 2.44.1 → 2.52.0 keine Änderungen
-
2.44.0
2024-02-23
- 2.29.3 → 2.43.7 keine Änderungen
-
2.29.2
2020-10-29
- 2.25.2 → 2.29.1 keine Änderungen
-
2.25.1
2020-02-17
-
2.25.0
2020-01-13
- 2.24.1 → 2.24.4 keine Änderungen
-
2.24.0
2019-11-04
- 2.22.1 → 2.23.4 keine Änderungen
-
2.22.0
2019-06-07
- 2.18.1 → 2.21.4 keine Änderungen
-
2.18.0
2018-06-21
- 2.16.6 → 2.17.6 keine Änderungen
-
2.15.4
2019-12-06
-
2.14.6
2019-12-06
-
2.13.7
2018-05-22
-
2.12.5
2017-09-22
- 2.10.5 → 2.11.4 keine Änderungen
-
2.9.5
2017-07-30
-
2.8.6
2017-07-30
- 2.3.10 → 2.7.6 keine Änderungen
-
2.2.3
2015-09-04
- 2.1.4 keine Änderungen
-
2.0.5
2014-12-17
SYNOPSIS
git filter-branch [--setup <command>] [--subdirectory-filter <directory>] [--env-filter <command>] [--tree-filter <command>] [--index-filter <command>] [--parent-filter <command>] [--msg-filter <command>] [--commit-filter <command>] [--tag-name-filter <command>] [--prune-empty] [--original <namespace>] [-d <directory>] [-f | --force] [--state-branch <branch>] [--] [<rev-list-options>…]
WARNUNG
git filter-branch hat eine Fülle von Fallstricken, die zu nicht offensichtlichen Umgestaltungen der beabsichtigten Verlaufsüberschreibung führen können (und Ihnen wenig Zeit zum Untersuchen solcher Probleme lassen, da es eine so erbärmliche Leistung aufweist). Diese Sicherheits- und Leistungsprobleme können nicht abwärtskompatibel behoben werden und werden daher nicht empfohlen. Bitte verwenden Sie ein alternatives Werkzeug zur Verlaufsfilterung wie z. B. git filter-repo. Wenn Sie git filter-branch weiterhin verwenden müssen, lesen Sie bitte sorgfältig SICHERHEIT (und LEISTUNG), um die Minenfelder von filter-branch kennenzulernen, und vermeiden Sie dann wachsam so viele der dort aufgeführten Gefahren, wie es vernünftigerweise möglich ist.
BESCHREIBUNG
Ermöglicht das Umschreiben des Git-Revisionsverlaufs durch Überschreiben der im <rev-list-options> genannten Zweige, wobei benutzerdefinierte Filter auf jede Revision angewendet werden. Diese Filter können jeden Baum modifizieren (z. B. eine Datei entfernen oder einen Perl-Rewrite auf alle Dateien anwenden) oder Informationen über jeden Commit. Andernfalls bleiben alle Informationen (einschließlich der ursprünglichen Commit-Zeiten oder Merge-Informationen) erhalten.
Der Befehl überschreibt nur die im Befehlszeile genannten *positiven* Referenzen (z. B. wenn Sie *a..b* übergeben, wird nur *b* überschrieben). Wenn Sie keine Filter angeben, werden die Commits ohne Änderungen neu committet, was normalerweise keine Auswirkungen hätte. Dennoch kann dies in Zukunft nützlich sein, um einige Git-Fehler oder ähnliches zu kompensieren, daher ist eine solche Verwendung zulässig.
HINWEIS: Dieser Befehl berücksichtigt die Datei .git/info/grafts und Referenzen im Namensraum refs/replace/. Wenn Sie Grafts oder Ersatzreferenzen definiert haben, macht die Ausführung dieses Befehls diese permanent.
WARNUNG! Der umgeschriebene Verlauf wird für alle Objekte andere Objektnamen haben und nicht mit dem ursprünglichen Zweig konvergieren. Sie werden den umgeschriebenen Zweig nicht einfach auf den ursprünglichen Zweig pushen und verteilen können. Bitte verwenden Sie diesen Befehl nicht, wenn Sie die vollen Auswirkungen nicht kennen, und vermeiden Sie ihn ohnehin, wenn ein einfacher einzelner Commit zur Behebung Ihres Problems ausreichen würde. (Siehe den Abschnitt "WIEDERHERSTELLUNG VON UPSTREAM-REBASES" in git-rebase[1] für weitere Informationen zum Umschreiben von veröffentlichten Verläufen.)
Verifizieren Sie immer, dass die umgeschriebene Version korrekt ist: Die ursprünglichen Referenzen, sofern sie sich von den umgeschriebenen unterscheiden, werden im Namensraum refs/original/ gespeichert.
Beachten Sie, dass diese Operation aufgrund ihrer sehr I/O-intensiven Natur möglicherweise eine gute Idee ist, das temporäre Verzeichnis mit der Option -d auf ein anderes Medium umzuleiten, z. B. auf tmpfs. Berichten zufolge ist die Geschwindigkeitssteigerung sehr bemerkenswert.
Filter
Die Filter werden in der unten aufgeführten Reihenfolge angewendet. Das Argument <Befehl> wird immer im Shell-Kontext mit dem Befehl eval ausgewertet (mit der bemerkenswerten Ausnahme des Commit-Filters aus technischen Gründen). Zuvor wird die Umgebungsvariable $GIT_COMMIT so gesetzt, dass sie die ID des umgeschriebenen Commits enthält. Außerdem werden GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL und GIT_COMMITTER_DATE aus dem aktuellen Commit übernommen und in die Umgebung exportiert, um die Autor- und Committer-Identitäten des durch git-commit-tree[1] nach Ausführung der Filter erstellten Ersatz-Commits zu beeinflussen.
Wenn eine Auswertung von <Befehl> einen Nicht-Null-Beendigungsstatus zurückgibt, wird die gesamte Operation abgebrochen.
Eine map-Funktion ist verfügbar, die ein Argument "original sha1 id" entgegennimmt und eine "rewritten sha1 id" ausgibt, wenn der Commit bereits umgeschrieben wurde, andernfalls die "original sha1 id"; die map-Funktion kann mehrere IDs auf separaten Zeilen zurückgeben, wenn Ihr Commit-Filter mehrere Commits erzeugt hat.
OPTIONEN
- --setup <Befehl>
-
Dies ist kein echter Filter, der für jeden Commit ausgeführt wird, sondern eine einmalige Einrichtung unmittelbar vor der Schleife. Daher sind noch keine commit-spezifischen Variablen definiert. Hier definierte Funktionen oder Variablen können in den folgenden Filterstufen verwendet oder modifiziert werden, mit Ausnahme des Commit-Filters aus technischen Gründen.
- --subdirectory-filter <Verzeichnis>
-
Nur den Verlauf berücksichtigen, der das angegebene Unterverzeichnis betrifft. Das Ergebnis enthält dieses Verzeichnis (und nur dieses) als Projektstammverzeichnis. Impliziert Zu Vorfahren umleiten.
- --env-filter <Befehl>
-
Dieser Filter kann verwendet werden, wenn Sie nur die Umgebung ändern müssen, in der der Commit durchgeführt wird. Insbesondere möchten Sie möglicherweise die Umgebungsadressen für Autor/Committer/Zeit ändern (siehe git-commit-tree[1] für Details).
- --tree-filter <Befehl>
-
Dies ist der Filter zum Umschreiben des Baumes und seines Inhalts. Das Argument wird in der Shell ausgewertet, wobei das Arbeitsverzeichnis auf die Wurzel des ausgecheckten Baumes gesetzt wird. Der neue Baum wird dann unverändert verwendet (neue Dateien werden automatisch hinzugefügt, verschwundene Dateien werden automatisch entfernt – weder .gitignore-Dateien noch andere Ignorierregeln **HABEN KEINEN EINFLUSS**!).
- --index-filter <Befehl>
-
Dies ist der Filter zum Umschreiben des Index. Er ist ähnlich dem Baumfilter, prüft aber den Baum nicht aus, was ihn viel schneller macht. Wird häufig mit
gitrm--cached--ignore-unmatch... verwendet, siehe BEISPIELE unten. Für komplizierte Fälle siehe git-update-index[1]. - --parent-filter <Befehl>
-
Dies ist der Filter zum Umschreiben der Elterliste des Commits. Er erhält die Elternzeichenkette über stdin und soll die neue Elternzeichenkette über stdout ausgeben. Die Elternzeichenkette hat das Format, das in git-commit-tree[1] beschrieben ist: leer für den initialen Commit, "-p parent" für einen normalen Commit und "-p parent1 -p parent2 -p parent3 …" für einen Merge-Commit.
- --msg-filter <Befehl>
-
Dies ist der Filter zum Umschreiben der Commit-Nachrichten. Das Argument wird in der Shell ausgewertet, wobei die ursprüngliche Commit-Nachricht auf Standardeingabe liegt; seine Standardausgabe wird als neue Commit-Nachricht verwendet.
- --commit-filter <Befehl>
-
Dies ist der Filter zur Durchführung des Commits. Wenn dieser Filter angegeben ist, wird er anstelle des Befehls git commit-tree aufgerufen, mit Argumenten der Form "<BAUM-ID> [(-p <ELTERN-COMMIT-ID>)…]" und der Log-Nachricht auf stdin. Die Commit-ID wird auf stdout erwartet.
Als spezielle Erweiterung kann der Commit-Filter mehrere Commit-IDs ausgeben; in diesem Fall haben die umgeschriebenen Kinder des ursprünglichen Commits sie alle als Eltern.
Sie können die Komfortfunktion map in diesem Filter und auch andere Komfortfunktionen verwenden. Zum Beispiel wird durch den Aufruf von skip_commit "$@" der aktuelle Commit ausgelassen (aber nicht seine Änderungen! Wenn Sie das möchten, verwenden Sie stattdessen git rebase).
Sie können auch
git_commit_non_empty_tree"$@"anstelle vongitcommit-tree"$@"verwenden, wenn Sie keine Commits mit einem einzelnen Elternteil behalten möchten und diese keine Änderungen am Baum vornehmen. - --tag-name-filter <Befehl>
-
Dies ist der Filter zum Umschreiben von Tag-Namen. Wenn er übergeben wird, wird er für jeden Tag-Ref aufgerufen, der auf ein umgeschriebenes Objekt zeigt (oder auf ein Tag-Objekt, das auf ein umgeschriebenes Objekt zeigt). Der ursprüngliche Tag-Name wird über die Standardeingabe übergeben, und der neue Tag-Name wird auf der Standardausgabe erwartet.
Die ursprünglichen Tags werden nicht gelöscht, können aber überschrieben werden; verwenden Sie "--tag-name-filter cat", um die Tags einfach zu aktualisieren. Seien Sie in diesem Fall sehr vorsichtig und stellen Sie sicher, dass Sie die alten Tags gesichert haben, falls die Konvertierung fehlgeschlagen ist.
Das fast korrekte Umschreiben von Tag-Objekten wird unterstützt. Wenn das Tag eine Nachricht hat, wird ein neues Tag-Objekt mit derselben Nachricht, demselben Autor und demselben Zeitstempel erstellt. Wenn das Tag eine Signatur hat, wird diese entfernt. Es ist per Definition unmöglich, Signaturen zu erhalten. Der Grund, warum dies "fast" korrekt ist, ist, dass idealerweise, wenn das Tag nicht geändert wurde (auf dasselbe Objekt zeigt, denselben Namen hat usw.), es jede Signatur beibehalten sollte. Das ist nicht der Fall, Signaturen werden immer entfernt, der Käufer sei gewarnt. Es gibt auch keine Unterstützung für die Änderung des Autors oder Zeitstempels (oder der Tag-Nachricht). Tags, die auf andere Tags zeigen, werden umgeschrieben, um auf den zugrunde liegenden Commit zu zeigen.
- --prune-empty
-
Einige Filter erzeugen leere Commits, die den Baum unverändert lassen. Diese Option weist git-filter-branch an, solche Commits zu entfernen, wenn sie genau einen oder null nicht bereinigte Eltern haben; Merge-Commits bleiben daher intakt. Diese Option kann nicht zusammen mit
--commit-filterverwendet werden, obwohl der gleiche Effekt durch die Verwendung der bereitgestellten Funktiongit_commit_non_empty_treein einem Commit-Filter erzielt werden kann. - --original <Namensraum>
-
Verwenden Sie diese Option, um den Namensraum festzulegen, in dem die ursprünglichen Commits gespeichert werden. Der Standardwert ist refs/original.
- -d <Verzeichnis>
-
Verwenden Sie diese Option, um den Pfad zum temporären Verzeichnis festzulegen, das zum Umschreiben verwendet wird. Beim Anwenden eines Baumfilters muss der Befehl den Baum vorübergehend in ein Verzeichnis auschecken, was bei großen Projekten erheblichen Speicherplatz beanspruchen kann. Standardmäßig geschieht dies im Verzeichnis
.git-rewrite/, Sie können diese Wahl jedoch mit diesem Parameter überschreiben. - -f
- --force
-
git filter-branch weigert sich zu starten, wenn ein temporäres Verzeichnis vorhanden ist oder wenn bereits Referenzen mit refs/original/ vorhanden sind, es sei denn, es wird erzwungen.
- --state-branch <branch>
-
Diese Option bewirkt, dass die Zuordnung von alten zu neuen Objekten beim Start von dem benannten Zweig geladen und beim Beenden als neuer Commit auf diesen Zweig gespeichert wird, was eine inkrementelle Bearbeitung großer Bäume ermöglicht. Wenn <branch> nicht existiert, wird es erstellt.
- <rev-list options>…
-
Argumente für git rev-list. Alle positiven Referenzen, die von diesen Optionen eingeschlossen werden, werden umgeschrieben. Sie können auch Optionen wie
--allangeben, aber Sie müssen--verwenden, um sie von den git filter-branch Optionen zu trennen. Impliziert Zu Vorfahren umleiten.
Zu Vorfahren umleiten
Durch die Verwendung von git-rev-list[1]-Argumenten, z. B. Pfadbegrenzern, können Sie die Menge der zu überschreibenden Revisionen einschränken. Positive Referenzen in der Befehlszeile werden jedoch unterschieden: Wir lassen sie nicht von solchen Begrenzern ausschließen. Zu diesem Zweck werden sie stattdessen so umgeschrieben, dass sie auf den nächsten Vorfahren zeigen, der nicht ausgeschlossen wurde.
BEENDIGUNGSSTATUS
Bei Erfolg ist der Beendigungsstatus 0. Wenn der Filter keine zu überschreibenden Commits findet, ist der Beendigungsstatus 2. Bei jedem anderen Fehler kann der Beendigungsstatus ein beliebiger anderer Nicht-Null-Wert sein.
BEISPIELE
Angenommen, Sie möchten eine Datei (die vertrauliche Informationen oder Urheberrechtsverletzungen enthält) aus allen Commits entfernen
git filter-branch --tree-filter 'rm filename' HEAD
Wenn die Datei jedoch im Baum eines bestimmten Commits abwesend ist, schlägt ein einfaches rm filename für diesen Baum und Commit fehl. Daher möchten Sie möglicherweise stattdessen rm -f filename als Skript verwenden.
Die Verwendung von --index-filter mit git rm ergibt eine signifikant schnellere Version. Wie bei der Verwendung von rm filename schlägt git rm --cached filename fehl, wenn die Datei im Baum eines Commits abwesend ist. Wenn Sie eine Datei "vollständig vergessen" möchten, spielt es keine Rolle, wann sie in den Verlauf gelangt ist, also fügen wir auch --ignore-unmatch hinzu.
git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
Nun erhalten Sie den umgeschriebenen Verlauf in HEAD gespeichert.
Um das Repository so umzuschreiben, als ob foodir/ sein Projektstammverzeichnis gewesen wäre, und allen anderen Verlauf zu verwerfen
git filter-branch --subdirectory-filter foodir -- --all
Damit können Sie z. B. ein Bibliotheksunterverzeichnis in ein eigenes Repository umwandeln. Beachten Sie das --, das filter-branch-Optionen von Revisionsoptionen trennt, und das --all, um alle Zweige und Tags umzuschreiben.
Um einen Commit (der typischerweise an der Spitze eines anderen Verlaufs steht) zum Elternteil des aktuellen initialen Commits zu machen, um den anderen Verlauf hinter dem aktuellen Verlauf einzufügen
git filter-branch --parent-filter 'sed "s/^\$/-p <graft-id>/"' HEAD
(wenn die Elternzeichenkette leer ist – was beim initialen Commit der Fall ist – fügen Sie graftcommit als Elternteil hinzu). Beachten Sie, dass dies einen Verlauf mit einer einzigen Wurzel annimmt (d. h. es gab keine Merges ohne gemeinsame Vorfahren). Wenn dies nicht der Fall ist, verwenden Sie
git filter-branch --parent-filter \ 'test $GIT_COMMIT = <commit-id> && echo "-p <graft-id>" || cat' HEAD
oder noch einfacher
git replace --graft $commit-id $graft-id git filter-branch $graft-id..HEAD
Um Commits, die von "Darl McBribe" verfasst wurden, aus dem Verlauf zu entfernen
git filter-branch --commit-filter ' if [ "$GIT_AUTHOR_NAME" = "Darl McBribe" ]; then skip_commit "$@"; else git commit-tree "$@"; fi' HEAD
Die Funktion skip_commit ist wie folgt definiert
skip_commit()
{
shift;
while [ -n "$1" ];
do
shift;
map "$1";
shift;
done;
}
Der Shift-Zauber wirft zuerst die Baum-ID und dann die -p-Parameter weg. Beachten Sie, dass dies Merges korrekt behandelt! Wenn Darl einen Merge zwischen P1 und P2 committet hat, wird dieser ordnungsgemäß weitergegeben und alle Kinder des Merges werden zu Merge-Commits mit P1, P2 als Eltern anstelle des Merge-Commits.
HINWEIS Die durch die Commits eingeführten Änderungen, die nicht durch nachfolgende Commits rückgängig gemacht werden, verbleiben im umgeschriebenen Zweig. Wenn Sie *Änderungen* zusammen mit den Commits verwerfen möchten, sollten Sie den interaktiven Modus von git rebase verwenden.
Sie können die Commit-Log-Nachrichten mit --msg-filter umschreiben. Zum Beispiel können git svn-id-Strings in einem von git svn erstellten Repository auf diese Weise entfernt werden.
git filter-branch --msg-filter ' sed -e "/^git-svn-id:/d" '
Wenn Sie Acked-by-Zeilen zu den letzten 10 Commits hinzufügen müssen (von denen keiner ein Merge ist), verwenden Sie diesen Befehl
git filter-branch --msg-filter ' cat && echo "Acked-by: Bugs Bunny <bunny@bugzilla.org>" ' HEAD~10..HEAD
Die Option --env-filter kann verwendet werden, um die Identität des Committers und/oder Autors zu ändern. Wenn Sie beispielsweise feststellen, dass Ihre Commits aufgrund einer falsch konfigurierten user.email die falsche Identität haben, können Sie vor der Veröffentlichung des Projekts eine Korrektur vornehmen, wie folgt:
git filter-branch --env-filter ' if test "$GIT_AUTHOR_EMAIL" = "root@localhost" then GIT_AUTHOR_EMAIL=john@example.com fi if test "$GIT_COMMITTER_EMAIL" = "root@localhost" then GIT_COMMITTER_EMAIL=john@example.com fi ' -- --all
Um das Umschreiben auf nur einen Teil des Verlaufs zu beschränken, geben Sie zusätzlich zum neuen Zweignamen einen Revisionsbereich an. Der neue Zweigname zeigt auf die oberste Revision, die eine git rev-list dieses Bereichs ausgeben wird.
Betrachten Sie diesen Verlauf
D--E--F--G--H
/ /
A--B-----C
Um nur die Commits D, E, F, G, H umzuschreiben, A, B und C aber unverändert zu lassen, verwenden Sie
git filter-branch ... C..H
Um die Commits E, F, G, H umzuschreiben, verwenden Sie eine dieser Optionen
git filter-branch ... C..H --not D git filter-branch ... D..H --not C
Um den gesamten Baum in ein Unterverzeichnis zu verschieben oder von dort zu entfernen
git filter-branch --index-filter \ 'git ls-files -s | sed "s-\t\"*-&newsubdir/-" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new \ git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
CHECKLISTE ZUM VERKLEINERN EINES REPOSITORIES
git-filter-branch kann verwendet werden, um eine Teilmenge von Dateien loszuwerden, normalerweise mit einer Kombination aus --index-filter und --subdirectory-filter. Die Leute erwarten, dass das resultierende Repository kleiner ist als das Original, aber Sie benötigen einige weitere Schritte, um es tatsächlich kleiner zu machen, da Git hart daran arbeitet, Ihre Objekte nicht zu verlieren, bis Sie es ihm sagen. Stellen Sie zuerst sicher, dass
-
Sie wirklich alle Varianten eines Dateinamens entfernt haben, wenn ein Blob im Laufe seines Lebens über seinen Namen verschoben wurde.
gitlog--name-only--follow--all--filenamekann Ihnen helfen, Umbenennungen zu finden. -
Sie wirklich alle Referenzen gefiltert haben: Verwenden Sie
--tag-name-filtercat----allbeim Aufrufen von git-filter-branch.
Dann gibt es zwei Möglichkeiten, ein kleineres Repository zu erhalten. Eine sicherere Methode ist das Klonen, das Ihr Original intakt lässt.
-
Klonen Sie es mit
gitclonefile:///pfad/zum/repo. Der Klon wird die entfernten Objekte nicht enthalten. Siehe git-clone[1]. (Beachten Sie, dass das Klonen mit einem einfachen Pfad alles hartlinkt!)
Wenn Sie es aus welchen Gründen auch immer wirklich nicht klonen wollen, überprüfen Sie stattdessen die folgenden Punkte (in dieser Reihenfolge). Dies ist ein sehr destruktiver Ansatz, also **machen Sie ein Backup** oder gehen Sie zurück zum Klonen. Sie wurden gewarnt.
-
Entfernen Sie die ursprünglichen Referenzen, die von git-filter-branch gesichert wurden: z. B.
gitfor-each-ref--format="%(refname)"refs/original/|xargs-n1gitupdate-ref-d. -
Verfallen Sie alle Reflogs mit
gitreflogexpire--expire=now--all. -
Sammeln Sie alle nicht referenzierten Objekte mit
gitgc--prune=now(oder wenn Ihr git-gc nicht neu genug ist, um Argumente für--prunezu unterstützen, verwenden Sie stattdessen git repack -ad; git prune).
LEISTUNG
Die Leistung von git-filter-branch ist glaciale langsam; sein Design macht es für eine abwärtskompatible Implementierung unmöglich, jemals schnell zu sein.
-
Beim Bearbeiten von Dateien prüft git-filter-branch designbedingt jeden einzelnen Commit aus, wie er im ursprünglichen Repository existierte. Wenn Ihr Repository
10^5Dateien und10^5Commits hat, aber jeder Commit nur fünf Dateien modifiziert, dann zwingt git-filter-branch Sie zu10^10Modifikationen, obwohl es (höchstens)5*10^5eindeutige Blobs gibt. -
Wenn Sie versuchen zu schummeln und git-filter-branch nur an Dateien arbeiten zu lassen, die in einem Commit modifiziert wurden, dann passieren zwei Dinge:
-
Sie stoßen auf Probleme mit Löschungen, wenn der Benutzer nur versucht, Dateien umzubenennen (da der Versuch, nicht existierende Dateien zu löschen, wie eine No-Op aussieht; es erfordert einige Tricks, um Löschungen bei Umbenennungen über willkürlich vom Benutzer bereitgestellte Shells hinweg abzubilden)
-
selbst wenn Sie bei der Chicanerie zum Zuordnen von Löschungen für Umbenennungen erfolgreich sind, verstoßen Sie technisch immer noch gegen die Abwärtskompatibilität, da Benutzer Dateien auf eine Weise filtern dürfen, die von der Topologie der Commits abhängt, anstatt nur basierend auf Dateiinhalten oder Namen zu filtern (obwohl dies in der Praxis noch nicht beobachtet wurde).
-
-
Selbst wenn Sie keine Dateien bearbeiten müssen, sondern nur z. B. einige umbenennen oder entfernen möchten und somit das Auschecken jeder Datei vermeiden können (d. h. Sie können --index-filter verwenden), übergeben Sie immer noch Shell-Schnipsel für Ihre Filter. Das bedeutet, dass Sie für jeden Commit ein vorbereitetes Git-Repository haben müssen, in dem diese Filter ausgeführt werden können. Das ist eine erhebliche Einrichtung.
-
Darüber hinaus werden pro Commit mehrere zusätzliche Dateien von git-filter-branch erstellt oder aktualisiert. Einige davon dienen der Unterstützung der von git-filter-branch bereitgestellten Komfortfunktionen (wie map()), während andere der Verfolgung des internen Zustands dienen (aber auch von Benutzerfiltern hätten zugegriffen werden können; einer der Regressionstests von git-filter-branch tut dies). Dies läuft im Wesentlichen darauf hinaus, das Dateisystem als IPC-Mechanismus zwischen git-filter-branch und den vom Benutzer bereitgestellten Filtern zu verwenden. Festplatten sind tendenziell ein langsamer IPC-Mechanismus, und das Schreiben dieser Dateien stellt auch eine erzwungene Synchronisationsstelle zwischen separaten Prozessen dar, auf die wir bei jedem Commit stoßen.
-
Die vom Benutzer bereitgestellten Shell-Befehle beinhalten wahrscheinlich eine Pipeline von Befehlen, was zur Erstellung vieler Prozesse pro Commit führt. Das Erstellen und Ausführen eines weiteren Prozesses dauert je nach Betriebssystem stark variierend, ist aber auf jeder Plattform sehr langsam im Vergleich zum Aufruf einer Funktion.
-
git-filter-branch selbst ist in Shell geschrieben, was eher langsam ist. Dies ist das einzige Leistungsproblem, das abwärtskompatibel behoben werden könnte, aber im Vergleich zu den oben genannten Problemen, die dem Design von git-filter-branch innewohnen, ist die Sprache des Tools selbst ein relativ geringfügiges Problem.
-
Nebenbemerkung: Bedauerlicherweise fixieren sich die Leute auf den in Shell geschriebenen Aspekt und fragen periodisch, ob git-filter-branch in einer anderen Sprache neu geschrieben werden könnte, um die Leistungsprobleme zu beheben. Das ignoriert nicht nur die größeren inhärenten Probleme mit dem Design, sondern würde auch weniger helfen, als Sie erwarten würden: Wenn git-filter-branch selbst nicht in Shell geschrieben wäre, könnten die Komfortfunktionen (map(), skip_commit() usw.) und das Argument
--setupnicht mehr einmal zu Beginn des Programms ausgeführt werden, sondern müssten stattdessen jedem Benutzerfilter vorangestellt werden (und somit bei jedem Commit erneut ausgeführt werden).
-
Das Werkzeug git filter-repo ist eine Alternative zu git-filter-branch, das nicht unter diesen Leistungsproblemen oder den Sicherheitsproblemen (unten erwähnt) leidet. Für diejenigen, deren bestehende Werkzeuge auf git-filter-branch angewiesen sind, bietet git filter-repo auch filter-lamely, einen Drop-in-Ersatz für git-filter-branch (mit einigen Vorbehalten). Während filter-lamely unter denselben Sicherheitsproblemen wie git-filter-branch leidet, lindert es zumindest die Leistungsprobleme ein wenig.
SICHERHEIT
git-filter-branch ist voller Fallstricke, die auf verschiedene Weise zu einer einfachen Beschädigung von Repositories führen oder zu einem Durcheinander führen können, das schlimmer ist als das, womit Sie begonnen haben.
-
Jemand kann einen Satz von "funktionierenden und getesteten Filtern" haben, die er dokumentiert oder einem Kollegen zur Verfügung stellt, der sie dann auf einem anderen Betriebssystem ausführt, wo dieselben Befehle nicht funktionieren/getestet werden (einige Beispiele in der git-filter-branch-Manpage sind ebenfalls davon betroffen). Unterschiede zwischen der Benutzerumgebung von BSD und GNU können wirklich schmerzhaft sein. Wenn man Glück hat, werden Fehlermeldungen ausgegeben. Aber genauso gut tun die Befehle entweder nicht die angeforderte Filterung oder beschädigen stillschweigend, indem sie unerwünschte Änderungen vornehmen. Die unerwünschte Änderung kann nur wenige Commits betreffen, daher ist sie auch nicht unbedingt offensichtlich. (Die Tatsache, dass Probleme nicht unbedingt offensichtlich sind, bedeutet, dass sie wahrscheinlich unbemerkt bleiben, bis der umgeschriebene Verlauf eine ganze Weile verwendet wird, an dem Punkt ist es wirklich schwer, einen weiteren Flaggen-Tag für eine weitere Umschreibung zu rechtfertigen.)
-
Dateinamen mit Leerzeichen werden oft von Shell-Snippets falsch behandelt, da sie Probleme für Shell-Pipelines verursachen. Nicht jeder ist mit find -print0, xargs -0, git-ls-files -z usw. vertraut. Selbst Leute, die damit vertraut sind, können davon ausgehen, dass solche Flags nicht relevant sind, weil jemand anderes solche Dateien in seinem Repository umbenannt hat, bevor die Person, die filtert, dem Projekt beigetreten ist. Und oft tun selbst diejenigen, die mit der Handhabung von Argumenten mit Leerzeichen vertraut sind, dies nur, weil sie nicht im Gedanken sind, alles zu bedenken, was schiefgehen könnte.
-
Nicht-ASCII-Dateinamen können stillschweigend entfernt werden, obwohl sie sich in einem gewünschten Verzeichnis befinden. Das Beibehalten nur gewünschter Pfade wird oft mit Pipelines wie
gitls-files|grep-v^WANTED_DIR/|xargsgitrmdurchgeführt. ls-files maskiert Dateinamen nur, wenn nötig, daher bemerken die Leute möglicherweise nicht, dass eine der Dateien nicht mit dem Regex übereinstimmt (zumindest nicht, bis es viel zu spät ist). Ja, jemand, der von core.quotePath weiß, kann dies vermeiden (es sei denn, er hat andere Sonderzeichen wie \t, \n oder "), und Leute, die ls-files -z mit etwas anderem als grep verwenden, können dies vermeiden, aber das bedeutet nicht, dass sie es tun werden. -
Ähnlich kann man beim Verschieben von Dateien feststellen, dass Dateinamen mit Nicht-ASCII- oder Sonderzeichen in einem anderen Verzeichnis landen, das ein doppeltes Anführungszeichen enthält. (Dies ist technisch gesehen dasselbe Problem wie oben mit Anführungszeichen, aber vielleicht eine interessante andere Art, wie es sich manifestiert hat und ein Problem darstellt.)
-
Es ist viel zu einfach, alte und neue Verläufe versehentlich zu vermischen. Das ist mit jedem Werkzeug immer noch möglich, aber git-filter-branch lädt geradezu dazu ein. Wenn man Glück hat, ist der einzige Nachteil, dass Benutzer frustriert sind, weil sie nicht wissen, wie sie ihr Repository verkleinern und die alten Sachen entfernen können. Wenn man Pech hat, vermischt man alte und neue Verläufe und hat am Ende mehrere "Kopien" jedes Commits, von denen einige unerwünschte oder sensible Dateien enthalten und andere nicht. Dies geschieht auf vielfältige Weise:
-
die Standardeinstellung, nur eine teilweise Verlaufsüberschreibung durchzuführen (--all ist nicht die Standardeinstellung und nur wenige Beispiele zeigen sie)
-
die Tatsache, dass keine automatische Bereinigung nach der Ausführung erfolgt
-
die Tatsache, dass --tag-name-filter (wenn zum Umbenennen von Tags verwendet) die alten Tags nicht löscht, sondern nur neue mit dem neuen Namen hinzufügt
-
die Tatsache, dass wenig Bildungsinformationen bereitgestellt werden, um Benutzer über die Auswirkungen einer Umschreibung zu informieren und wie man das Vermischen von alten und neuen Verläufen vermeidet. Zum Beispiel wird in dieser Manpage besprochen, wie Benutzer verstehen müssen, dass sie ihre Änderungen für alle ihre Zweige auf den neuen Verlauf neu basieren müssen (oder löschen und neu klonen), aber das ist nur eine von mehreren zu berücksichtigenden Fragen. Weitere Details finden Sie im Abschnitt "DISKUSSION" der Handbuchseite von git filter-repo.
-
-
Annotierte Tags können versehentlich in Lightweight-Tags umgewandelt werden, entweder aus einem von zwei Gründen:
-
Jemand kann eine Verlaufsüberschreibung durchführen, feststellen, dass er es vermasselt hat, aus den Backups in refs/original/ wiederherstellen und dann seinen git-filter-branch-Befehl erneut ausführen. (Das Backup in refs/original/ ist kein echtes Backup; es dereferenziert zuerst Tags.)
-
git-filter-branch mit --tags oder --all in Ihren <rev-list-options> ausführen. Um annotierte Tags als annotierte beizubehalten, müssen Sie --tag-name-filter verwenden (und dürfen nicht aus refs/original/ in einer zuvor fehlgeschlagenen Umschreibung wiederhergestellt haben).
-
-
Alle Commit-Nachrichten, die eine Kodierung angeben, werden durch die Umschreibung beschädigt; git-filter-branch ignoriert die Kodierung, nimmt die ursprünglichen Bytes und übergibt sie an commit-tree, ohne ihm die korrekte Kodierung mitzuteilen. (Dies geschieht, unabhängig davon, ob --msg-filter verwendet wird oder nicht.)
-
Commit-Nachrichten (auch wenn sie alle UTF-8 sind) werden standardmäßig beschädigt, da sie nicht aktualisiert werden – alle Verweise auf andere Commit-Hashes in Commit-Nachrichten beziehen sich nun auf nicht mehr existierende Commits.
-
Es gibt keine Einrichtungen, die den Benutzern helfen, unerwünschte Artefakte zu finden, die sie löschen sollten, was bedeutet, dass sie eher unvollständige oder teilweise Bereinigungen haben, die manchmal zu Verwirrung führen und Leute Zeit damit verschwenden, Dinge zu verstehen. (Zum Beispiel neigen Leute dazu, nur nach großen Dateien zu suchen, anstatt nach großen Verzeichnissen oder Erweiterungen, und nachdem sie dies getan haben, bemerken Leute, die später das neue Repository verwenden und den Verlauf durchgehen, ein Build-Artefaktverzeichnis, das einige Dateien, aber nicht andere hat, oder einen Cache von Abhängigkeiten (node_modules oder ähnlich), der nie funktionsfähig gewesen sein kann, da ihm einige Dateien fehlen.)
-
Wenn --prune-empty nicht angegeben ist, kann der Filterprozess Scharen verwirrender leerer Commits erzeugen.
-
Wenn --prune-empty angegeben ist, werden auch absichtlich platzierte leere Commits aus der Zeit vor dem Filtervorgang bereinigt, anstatt nur Commits zu bereinigen, die aufgrund von Filterregeln leer geworden sind.
-
Wenn --prune-empty angegeben ist, werden manchmal leere Commits übersehen und trotzdem belassen (ein eher seltener Fehler, aber es kommt vor...)
-
Ein geringfügiges Problem, aber Benutzer, die das Ziel haben, alle Namen und E-Mails in einem Repository zu aktualisieren, können zu --env-filter geführt werden, der nur Autoren und Committer aktualisiert und Tagger übersieht.
-
Wenn der Benutzer einen --tag-name-filter angibt, der mehrere Tags auf denselben Namen abbildet, wird keine Warnung oder kein Fehler ausgegeben; git-filter-branch überschreibt einfach jeden Tag in einer undokumentierten, vordefinierten Reihenfolge, was am Ende zu nur einem Tag führt. (Ein Regressionstest von git-filter-branch erfordert dieses überraschende Verhalten.)
Auch die schlechte Leistung von git-filter-branch führt oft zu Sicherheitsproblemen
-
Das Erstellen des korrekten Shell-Snippets zur Durchführung der gewünschten Filterung ist manchmal schwierig, es sei denn, Sie nehmen nur eine triviale Änderung vor, wie z. B. das Löschen einiger Dateien. Leider lernen die Leute oft, ob das Snippet richtig oder falsch ist, indem sie es ausprobieren, aber die Richtigkeit oder Falschheit kann je nach besonderen Umständen variieren (Leerzeichen in Dateinamen, Nicht-ASCII-Dateinamen, seltsame Autorennamen oder E-Mails, ungültige Zeitzonen, Vorhandensein von Grafts oder Ersetzungsobjekten usw.), was bedeutet, dass sie möglicherweise lange warten, einen Fehler erhalten und dann neu starten müssen. Die Leistung von git-filter-branch ist so schlecht, dass dieser Zyklus schmerzhaft ist, was die Zeit für sorgfältige Überprüfung reduziert (ganz zu schweigen davon, was dies mit der Geduld der Person, die die Umschreibung durchführt, macht, selbst wenn sie technisch mehr Zeit zur Verfügung hat). Dieses Problem wird zusätzlich dadurch verschärft, dass Fehler von defekten Filtern möglicherweise erst lange danach angezeigt werden und/oder in einem Meer von Ausgabe untergehen. Noch schlimmer ist, dass defekte Filter oft nur zu stillschweigenden falschen Umschreibungen führen.
-
Zu allem Überfluss, selbst wenn Benutzer schließlich funktionierende Befehle finden, möchten sie diese natürlich teilen. Aber sie sind sich möglicherweise nicht bewusst, dass ihr Repository nicht einige Sonderfälle hatte, die jemand anderes hat. Wenn also jemand mit einem anderen Repository dieselben Befehle ausführt, trifft er auf die oben genannten Probleme. Oder der Benutzer führt einfach Befehle aus, die wirklich für Sonderfälle geprüft wurden, aber er führt sie auf einem anderen Betriebssystem aus, wo sie nicht funktionieren, wie oben erwähnt.
GIT
Teil der git[1] Suite