Wie "Rebases Tags" in Git?

9

Angenommen, ich besitze das folgende einfache git-Repository: eine einzelne Verzweigung, einige werden nacheinander übergeben, einige davon wurden mit annotierten -Tags markiert, nachdem sie alle übergeben wurden, und dann eins Tag Ich entscheide, dass ich das erste Commit ändern möchte (was übrigens nicht markiert ist, wenn das etwas ändert). Also lasse ich git rebase --interactive --root laufen und markiere 'edit' für das initiale Commit, ändere etwas darin und git rebase --continue . Jetzt sind alle Commits in meinem Repository neu erstellt worden, daher haben sich ihre Sha1s geändert. Die Tags, die ich erstellt habe, sind jedoch völlig unverändert und verweisen immer noch auf das sha1 der vorherigen Commits.

Gibt es eine automatische Möglichkeit, die Tags auf die entsprechenden Commits zu aktualisieren, die beim Rebasieren erstellt werden?

Einige Leute schlagen vor, git filter-branch --tag-name-filter cat -- --tags zu verwenden, aber das warnt mich davor, dass jedes meiner Tags unverändert ist und sagt dann, dass jedes meiner Tags in sich selbst geändert wird (gleicher Tag-Name und gleicher Commit-Hash). Und trotzdem sagt git show --tags , dass die Tags immer noch auf die alten Commits zeigen.

    
Jake Parsons 05.11.2015, 00:02
quelle

3 Antworten

11

In gewisser Hinsicht ist es zu spät (aber warten Sie, es gibt gute Nachrichten). Der Code filter-branch kann die Tags anpassen, da er während der Filterung eine Zuordnung von alt-sha1 zu neu-sha1 beibehält.

Tatsächlich verwenden sowohl filter-branch als auch rebase die gleiche Grundidee, nämlich dass jedes Commit kopiert wird , indem der ursprüngliche Inhalt erweitert, die gewünschten Änderungen vorgenommen und dann ein neues Commit aus dem Ergebnis. Dies bedeutet, dass es bei jedem Kopierschritt trivial ist, das & lt; alt-sha1, new-sha1 & gt; Zu einer Datei pairen, und wenn Sie fertig sind, reparieren Sie die Referenzen, indem Sie die new-sha1 von ihrer alten-sha1 nachschlagen. Sobald alle Verweise fertig sind, sind Sie der neuen Nummerierung verpflichtet und Sie entfernen das Mapping.

Die Karte ist jetzt verschwunden, daher "in gewisser Hinsicht ist es zu spät".

Zum Glück ist es nicht zu spät. :-) Ihre Rebase ist wiederholbar, oder zumindest sind die wichtigsten Teile davon wahrscheinlich. Außerdem, wenn Ihr Rebase einfach genug war, müssen Sie es vielleicht gar nicht wiederholen.

Schauen wir uns den "wiederholten" Gedanken an. Wir haben einen originalen Graphen G beliebiger Form:

%Vor%

(whoa, eine fliegende Untertasse!). Wir haben eine git rebase --root auf (einem Teil davon) gemacht, indem (einige oder alle) Commits kopiert wurden (Merges beibehalten oder nicht), um ein neues Graph G 'zu erhalten:

%Vor%

Ich habe dieses Teilen nur den ursprünglichen Wurzelknoten gezeichnet (und jetzt ist es ein Segelboot mit einem Kran, anstatt einer fliegenden Untertasse). Es könnte mehr teilen oder weniger sein. Einige der alten Knoten wurden möglicherweise komplett unreferenziert und wurden daher als Müll gesammelt (wahrscheinlich nicht: die Reflogs sollten alle ursprünglichen Knoten für mindestens 30 Tage am Leben erhalten). Aber auf jeden Fall haben wir immer noch Tags, die in einen "alten G-Teil" von G 'zeigen, und diese Referenzen garantieren, dass diese Knoten und alle ihre Eltern still sind im neuen G '.

Wenn wir also wissen, wie die ursprüngliche Rebase gemacht wurde, können wir sie im Subgraphen von G 'wiederholen, der der wichtige Teil von G ist. Wie schwer oder einfach ist das und welche Befehle sollten verwendet werden? Um das zu tun, hängt es davon ab, ob das ursprüngliche G in G 'ist, was der Rebasenkommando war, wie viel G' das ursprüngliche G überlagert und mehr (seit git rev-list , was unser Schlüssel zum Erhalt einer Liste von Knoten ist , hat wahrscheinlich keine Möglichkeit, zwischen den Knoten "Original, was-in-G" und "neu zu G" zu unterscheiden. Aber es kann wahrscheinlich getan werden: Es ist nur eine kleine Sache der Programmierung, an diesem Punkt.

Wenn Sie es wiederholen, möchten Sie dieses Mal das Mapping behalten, vor allem, wenn das resultierende Diagramm G '' G 'nicht vollständig überlagert, denn Sie brauchen jetzt nicht die Karte selbst, sondern ein < em> Projektion dieser Karte, von G in G '.

Wir geben jedem Knoten im Original G einfach eine eindeutige relative Adresse (z. B. "vom Tip, finde den Eltern-Commit # 2; von diesem Commit find den Eltern-Commit # 1; von diesem Commit ...") und dann finde die entsprechende relative Adresse in G ''. Dadurch können wir die kritischen Teile der Karte neu erstellen.

Abhängig von der Einfachheit der ursprünglichen Rebase können wir möglicherweise direkt zu dieser Phase springen. Wenn wir zum Beispiel sicher sind, dass der gesamte Graph ohne Abflachung kopiert wurde (so dass wir zwei unabhängige fliegende Untertassen haben), dann ist die relative Adresse für das Tag T in G die relative Adresse, die wir wollen in G ', und jetzt ist es trivial, diese relative Adresse zu verwenden, um ein neues Tag zu machen, das auf das kopierte Commit zeigt.

Großes Update basierend auf neuen Informationen

Mit den zusätzlichen Informationen, dass das ursprüngliche Diagramm vollständig linear war und wir jedes Commit kopiert haben, können wir eine sehr einfache Strategie verwenden. Wir müssen immer noch die Karte rekonstruieren, aber jetzt ist es einfach, da jedes alte Commit genau ein neues Commit hat, das einen linearen Abstand (der einfach als eine einzelne Zahl darzustellen ist) von jedem Ende des ursprünglichen Graphen hat (ich werde Verwenden Sie Abstand von der Spitze).

Das heißt, das alte Diagramm sieht so aus, mit nur einem Zweig:

%Vor%

Die Tags zeigen einfach auf eines der Commits (über ein mit Anmerkungen versehenes Tag-Objekt), z. B. das Tag foo verweist auf ein Objekt mit annotiertem Tag, das auf commit W verweist. Wir stellen dann fest, dass W vier Commits von Z zurückgibt.

Das neue Diagramm sieht genau gleich aus, außer dass jedes Commit durch seine Kopie ersetzt wurde. Nennen wir diese A' , B' usw. bis Z' . Die (einzige) Verzweigung zeigt auf das am weitesten oben liegende Festschreiben, d. H.% Co_de%. Wir möchten das ursprüngliche Tag Z' so anpassen, dass wir ein neues Objekt für annotierte Tags haben, das auf foo zeigt.

Wir benötigen die SHA-1-ID des ursprünglichen Tip-Most-Commits. Dies sollte im Reflog für den (einzelnen) Zweig leicht zu finden sein und ist wahrscheinlich einfach W' (obwohl das davon abhängt, wie oft du den Zweig seither optimiert hast; und wenn es neue Commits gibt, die du seit dem Rebasieren hinzugefügt hast, wir müssen diese auch berücksichtigen).Es kann auch in der speziellen ref master@{1} sein, die ORIG_HEAD zurücklässt, falls Sie das Rebase-Ergebnis nicht mögen.

Nehmen wir an, dass git rebase die richtige ID ist und keine neuen Commits vorhanden sind. Dann:

%Vor%

würde diese ID in master@{1} speichern.

Wenn wir die vollständige Karte erstellen wollten, würde dies Folgendes tun:

%Vor%

(die Ausgabe für beide Dateien sollte die gleiche sein; wenn nicht, ist hier irgendeine Annahme falsch; in der Zwischenzeit gebe ich auch die Shell $orig_master Präfix aus, da der Rest wirklich in ein Skript geschrieben werden soll , auch für den einmaligen Gebrauch, im Falle von Tippfehlern und Notwendigkeit für Verbesserungen)

%Vor%

(dies ist ziemlich ungetestet, ist dazu gedacht, die beiden Dateien zusammenzufassen - eine Art Shell-Version von Python $ in den beiden Listen - um das Mapping zu erhalten). Aber wir brauchen das Mapping eigentlich nicht, wir brauchen nur die "Entfernung von der Spitze", also tue ich so, als ob wir hier nicht gestört hätten.

Jetzt müssen wir über alle Tags iterieren:

%Vor%

Wir müssen noch die beiden Shell-Funktionen zip und adj_anno_tag schreiben. Zuerst aber schreiben wir eine Shell-Funktion, die die neue ID mit der alten ID erzeugt, d. H. Die Zuordnung nachschlägt. Wenn wir eine echte Zuordnungsdatei verwenden würden, würden wir grep oder awk für die erste Eingabe verwenden und dann die zweite drucken. Bei Verwendung der sleazy Single-Old-File-Methode ist jedoch die Zeilennummer der passenden ID, die wir mit adj_lightweight_tag erhalten können:

%Vor%

Der Fall WARNING sollte niemals passieren, und die rev-Parse sollte nie fehlschlagen, aber wir sollten wahrscheinlich den Rückgabe-Status dieser Shell-Funktion überprüfen.

Der leichtgewichtige Tag Updater ist jetzt ziemlich trivial:

%Vor%

Das Aktualisieren eines kommentierten Tags ist schwieriger, aber wir können Code aus grep -n stehlen. Ich werde hier nicht alles zitieren; stattdessen gebe ich dir nur dieses bisschen:

%Vor%

und diese Anweisungen: Suchen Sie nach dem zweiten Vorkommen von git filter-branch und notieren Sie das git for-each-ref piped an git cat-file mit dem Ergebnis an sed , das die Shell-Variable git mktag festlegt.

Dies ist, was wir brauchen, um das Tag-Objekt zu kopieren. Die neue Kopie muss auf das gefundene Objekt zeigen, indem $ (map_sha1) für das Commit verwendet wird, auf das das alte Tag zeigt. Wir können feststellen, dass Commit auf die gleiche Weise wie new_sha1 ausgeführt wird, indem filter-branch verwendet wird.

(Übrigens, wenn ich diese Antwort schreibe und mir das Filter-Branch-Skript anschaue, fällt mir ein, dass es einen Filter in filter-branch gibt, den wir in unseren Post-Rebase-Tag-Fixup-Code importieren: wenn ein existierender mit Anmerkungen versehene Tags verweisen auf ein anderes Tag, wir beheben es nicht. Wir korrigieren nur Lightweight-Tags und -Tags, die direkt auf Commits verweisen.)

Beachten Sie, dass keiner der obigen Beispielcodes tatsächlich getestet wird und dass es zu einem allgemeineren Skript wird (das nach einer eventuellen Umbenennung ausgeführt werden könnte oder noch besser, die in die interaktive Umbenennung selbst integriert wird) angemessene Menge an zusätzlicher Arbeit.

    
torek 05.11.2015, 06:07
quelle
3

Dank torek 's detailliertem Durchlauf habe ich eine Implementierung zusammengestellt.

%Vor%     
Laura A. Rivera 26.04.2017 10:24
quelle
1

Sie können git rebasetags

verwenden

Sie verwenden genau so, wie Sie git rebase

verwenden würden

git rebasetags <rebase args>

Wenn die Rebase interaktiv ist, wird Ihnen eine Bash-Shell angezeigt, in der Sie die Änderungen vornehmen können. Beim Verlassen dieser Shell werden die Tags wiederhergestellt.

Von diesem Post

    
nachoparker 14.08.2017 21:43
quelle

Tags und Links