Ich habe vorher gefragt, wie die ersten beiden Commits in einem Git-Repository gequetscht werden sollen.
Die Lösungen sind zwar ziemlich interessant und nicht wirklich so verwirrend wie einige andere Dinge in Git, aber sie sind immer noch ein bisschen sprichwörtlich verletzt, wenn Sie den Vorgang während der Entwicklung Ihres Projekts viele Male wiederholen müssen.
Daher möchte ich lieber nur einmal Schmerzen haben und dann die standardmäßige interaktive Basis für immer verwenden können.
Was ich dann tun möchte, ist ein leeres anfängliches Commit, das nur zum Zweck der Ersten existiert. Kein Code, kein Nichts. Nehmen Sie einfach Platz ein, damit es die Basis für Rebase sein kann.
Meine Frage ist dann, ob ich bei einem vorhandenen Repository ein neues, leeres Commit vor dem ersten einfügen und alle anderen nach vorne verschieben kann.
Antworten:
Es gibt zwei Schritte, um dies zu erreichen:
Wir werden das neue leere Commit der Einfachheit halber in einen temporären Zweig stellen
newroot
.1. Erstellen Sie ein neues leeres Commit
Es gibt verschiedene Möglichkeiten, dies zu tun.
Verwenden Sie nur Sanitär
Der sauberste Ansatz besteht darin, Git's Sanitär zu verwenden, um direkt ein Commit zu erstellen, wodurch vermieden wird, dass die Arbeitskopie oder der Index berührt wird oder welcher Zweig ausgecheckt wird usw.
Erstellen Sie ein Baumobjekt für ein leeres Verzeichnis:
Wickeln Sie ein Commit darum:
Erstellen Sie einen Verweis darauf:
Sie können den gesamten Vorgang natürlich in einen Einzeiler umwandeln, wenn Sie Ihre Shell gut genug kennen.
Ohne Sanitär
Mit regulären Porzellanbefehlen können Sie
newroot
ohne guten Grund kein leeres Commit erstellen, ohne den Zweig auszuchecken und den Index und die Arbeitskopie wiederholt zu aktualisieren. Einige mögen dies jedoch leichter verstehen:Beachten Sie, dass Sie bei sehr alten Versionen von Git, denen der
--orphan
Wechsel zu fehltcheckout
, die erste Zeile durch folgende ersetzen müssen:2. Schreiben Sie den Verlauf neu, um mit diesem leeren Commit zu beginnen
Sie haben hier zwei Möglichkeiten: Neubasieren oder ein sauberes Umschreiben des Verlaufs.
Wiederherstellen
Dies hat den Vorteil der Einfachheit. Es werden jedoch auch der Name und das Datum des Committers bei jedem letzten Commit in der Verzweigung aktualisiert.
Bei einigen Edge-Case-Historien kann dies sogar aufgrund von Zusammenführungskonflikten fehlschlagen - trotz der Tatsache, dass Sie auf ein Commit zurückgreifen, das nichts enthält.
Geschichte neu schreiben
Der sauberere Ansatz besteht darin, den Zweig neu zu schreiben. Im Gegensatz zu mit
git rebase
müssen Sie nachschlagen, von welchem Commit Ihre Niederlassung ausgeht:Das Umschreiben erfolgt natürlich im zweiten Schritt; Es ist der erste Schritt, der einer Erklärung bedarf. Was
git replace
tut , ist es Git sagt , dass , wenn es einen Verweis auf ein Objekt sieht man ersetzt wollen, Git statt auf dem Ersatz des Objekts aussehen soll.Mit dem
--graft
Schalter sagen Sie etwas anderes als normal. Sie sagen, dass Sie noch kein Ersatzobjekt haben, aber Sie möchten das<currentroot>
Festschreibungsobjekt durch eine exakte Kopie von sich selbst ersetzen , außer dass die übergeordneten Festschreibungen der Ersetzung die von Ihnen aufgelisteten sein sollten (dh dienewroot
Festschreibung) ). Anschließendgit replace
wird dieses Commit für Sie erstellt und dieses Commit als Ersatz für Ihr ursprüngliches Commit deklariert.Wenn Sie nun a ausführen
git log
, werden Sie feststellen, dass die Dinge bereits so aussehen, wie Sie es möchten: Der Zweig beginnt beinewroot
.Beachten Sie jedoch, dass
git replace
der Verlauf dadurch weder geändert noch aus Ihrem Repository weitergegeben wird. Es wird lediglich eine lokale Umleitung zu Ihrem Repository von einem Objekt zu einem anderen hinzugefügt. Dies bedeutet, dass niemand sonst die Wirkung dieses Ersatzes sieht - nur Sie.Deshalb ist der
filter-branch
Schritt notwendig. Mitgit replace
erstellen Sie eine exakte Kopie mit angepassten übergeordneten Commits für das Root-Commit.git filter-branch
Wiederholt diesen Vorgang dann auch für alle folgenden Commits. Hier wird die Geschichte tatsächlich neu geschrieben, damit Sie sie teilen können.quelle
--onto newroot
Option ist überflüssig. Sie können darauf verzichten, da das Argument, das Sie übergeben,newroot
mit dem vorgelagerten Argument identisch istnewroot
.git-checkout
hatte dieser Schalter. Ich habe es aktualisiert, um diesen Ansatz zuerst zu erwähnen, danke für den Zeiger.git rebase --merge -s recursive -X theirs --onto newroot --root master
, lösen Sie alle Konflikte automatisch (siehe diese Antwort). @ AlexanderKuzinZusammenführung der Antworten von Aristoteles Pagaltzis und Uwe Kleine-König und des Kommentars von Richard Bronosky.
(nur um alles an einem Ort zu platzieren)
quelle
git rebase newroot master
wegen eines Fehlers in ändern .Ich mag Aristoteles 'Antwort. Es wurde jedoch festgestellt, dass ein Filterzweig für ein großes Repository (> 5000 Commits) aus mehreren Gründen besser funktioniert als eine Neubasis. 1) Er ist schneller. 2) Bei einem Zusammenführungskonflikt ist kein menschliches Eingreifen erforderlich. 3) Es kann die Tags umschreiben und sie beibehalten. Beachten Sie, dass die Filterverzweigung funktioniert, da keine Frage zum Inhalt jedes Commits besteht - es ist genau das gleiche wie vor dieser 'Rebase'.
Meine Schritte sind:
Beachten Sie, dass die Optionen '--tag-name-filter cat' bedeuten, dass Tags neu geschrieben werden, um auf die neu erstellten Commits zu verweisen.
quelle
Ich habe Teile der Antwort von Aristoteles und Kent erfolgreich verwendet:
Dadurch werden
master
zusätzlich zu den Tags auch alle Zweige (nicht nur ) neu geschrieben.quelle
refs/original/
und löscht jede Referenz. Die Refs, die gelöscht werden, sollten bereits von einem anderen Zweig referenziert werden, damit sie nicht wirklich verschwinden, sondern nurrefs/original/
entfernt werden.timedatectl set-time '2017-01-01 00:00:00'
zu geben ,newroot
einen alten Zeitstempel.git rebase --root --onto $emptyrootcommit
sollte den Trick leicht machen
quelle
$emptyrootcommit
ist eine Shell-Variable, die sich sicherlich zu nichts ausdehnt?Ich denke, dass die Verwendung von
git replace
undgit filter-branch
eine bessere Lösung ist als die Verwendung vongit rebase
:Die Idee dahinter ist:
git filter-branch
Hier ist ein Skript für die 2 ersten Schritte:
Sie können dieses Skript ohne Risiko ausführen (auch wenn es eine gute Idee ist, ein Backup zu erstellen, bevor Sie eine Aktion ausführen, die Sie noch nie zuvor durchgeführt haben;)). Wenn das Ergebnis nicht das erwartete ist, löschen Sie einfach die im Ordner erstellten Dateien
.git/refs/replace
und versuchen Sie es erneut. )Nachdem Sie überprüft haben, ob der Status des Repositorys Ihren Erwartungen entspricht, führen Sie den folgenden Befehl aus, um den Verlauf aller Zweige zu aktualisieren :
Jetzt müssen Sie zwei Historien sehen, die alte und die neue (
filter-branch
weitere Informationen finden Sie in der Hilfe ). Sie können die 2 vergleichen und erneut prüfen, ob alles in Ordnung ist. Wenn Sie zufrieden sind, löschen Sie die nicht mehr benötigten Dateien:Sie können zu Ihrem
master
Zweig zurückkehren und den temporären Zweig löschen:Jetzt sollte alles erledigt sein;)
quelle
Ich war aufgeregt und habe eine 'idempotente' Version dieses netten Skripts geschrieben ... es wird immer das gleiche leere Commit einfügen, und wenn Sie es zweimal ausführen, werden Ihre Commit-Hashes nicht jedes Mal geändert. Also, hier ist meine Einstellung zu git-insert-empty-root :
Lohnt sich die zusätzliche Komplexität? vielleicht nicht, aber ich werde diesen verwenden.
Dies sollte es auch ermöglichen, diesen Vorgang für mehrere geklonte Kopien des Repos durchzuführen und die gleichen Ergebnisse zu erzielen, damit sie weiterhin kompatibel sind ... Testen ... ja, es funktioniert, aber Sie müssen auch Ihre löschen und hinzufügen wieder Fernbedienungen, zB:
quelle
So fügen Sie zu Beginn eines Repositorys ein leeres Commit hinzu, wenn Sie vergessen haben, unmittelbar nach "git init" ein leeres Commit zu erstellen:
quelle
Folgendes habe ich mir ausgedacht:
quelle
Hier ist mein
bash
Skript basierend auf Kents Antwort mit Verbesserungen:master
, wenn es fertig ist;git checkout --orphan
arbeite aber nur mit einer Verzweigung, nicht mit einem Status mit getrenntem Kopf. Daher wurde sie lange genug ausgecheckt, um das neue Root-Commit durchzuführen, und dann gelöscht.filter-branch
(Kent hat dort einen Platzhalter zum manuellen Ersetzen gelassen).filter-branch
Operation schreibt nur die lokalen Zweige neu, nicht auch die Fernbedienungenquelle
So wechseln Sie das Root-Commit:
Erstellen Sie zunächst das gewünschte Commit als erstes.
Zweitens ändern Sie die Reihenfolge der Commits mit:
git rebase -i --root
Ein Editor mit den Commits wird bis zum Root-Commit angezeigt.
Wählen Sie 1234 alte Root-Nachricht
Wählen Sie 0294 Ein Commit in der Mitte
Wählen Sie 5678 Commit aus, das Sie an der Wurzel ablegen möchten
Sie können dann das gewünschte Commit zuerst setzen, indem Sie es in die erste Zeile setzen. Im Beispiel:
Wählen Sie 5678 Commit aus, das Sie an der Wurzel ablegen möchten
Wählen Sie 1234 alte Root-Nachricht
Wählen Sie 0294 Ein Commit in der Mitte
Beenden Sie den Editor. Die Festschreibungsreihenfolge hat sich geändert.
PS: Um den Editor zu ändern, den Git verwendet, führen Sie Folgendes aus:
git config --global core.editor name_of_the_editor_program_you_want_to_use
quelle
Das Neueste und Beste kombinieren. Keine Nebenwirkungen, keine Konflikte, Tags behalten.
Beachten Sie, dass Sie auf GitHub CI-Ausführungsdaten verlieren und PR möglicherweise durcheinander kommt, wenn nicht auch andere Zweige repariert werden.
quelle
Folgende Antwort Aristoteles Pagaltzis und andere, aber mit einfacheren Befehlen
Beachten Sie, dass Ihr Repo keine lokalen Änderungen enthalten sollte, die darauf warten, festgeschrieben zu werden.
Note
git checkout --orphan
wird bei neuen Versionen von Git funktionieren, denke ich.Beachten Sie, dass die meiste Zeit
git status
nützliche Hinweise gibt.quelle
Starten Sie ein neues Repository.
Setzen Sie Ihr Datum auf das gewünschte Startdatum zurück.
Tun Sie alles so, wie Sie es sich gewünscht haben, und passen Sie die Systemzeit an, um zu reflektieren, wann Sie es gewünscht haben. Ziehen Sie nach Bedarf Dateien aus dem vorhandenen Repository, um unnötiges Tippen zu vermeiden.
Wenn Sie heute ankommen, tauschen Sie die Repositorys aus und Sie sind fertig.
Wenn Sie nur verrückt (etabliert), aber einigermaßen intelligent sind (wahrscheinlich, weil Sie eine gewisse Menge an Intelligenz benötigen, um sich verrückte Ideen wie diese auszudenken), werden Sie den Prozess per Skript ausführen.
Das macht es auch schöner, wenn Sie entscheiden, dass die Vergangenheit in einer Woche auf andere Weise geschehen soll.
quelle
Ich weiß, dass dieser Beitrag alt ist, aber diese Seite ist die erste, wenn "Google einfügen" gegoogelt wird.
Warum einfache Dinge kompliziert machen?
Sie haben ABC und möchten ABZC.
git rebase -i trunk
(oder irgendetwas vor B)git add ..
git commit
(git commit --amend
wodurch B bearbeitet und nicht Z erstellt wird)[Sie können hier so viele festlegen,
git commit
wie Sie möchten, um weitere Commits einzufügen. Natürlich können Sie Probleme mit Schritt 5 haben, aber das Lösen von Zusammenführungskonflikten mit Git ist eine Fähigkeit, die Sie haben sollten. Wenn nicht, üben Sie!]git rebase --continue
Einfach, nicht wahr?
Wenn Sie verstehen
git rebase
, sollte das Hinzufügen eines Root-Commits kein Problem sein.Viel Spaß mit git!
quelle
git rebase
kann das nicht.