Obwohl ich zwei Unterabschnitte involviere, frage ich das als eine kombinierte Frage, weil die Art, wie es in Teile zerlegt wird, nicht wichtig ist. Ich bin offen für verschiedene Wege, um zu erreichen, was ich will, solange das Endergebnis die gesamte sinnvolle Geschichte und die Fähigkeit zum Ausprobieren, Studieren und Erstellen / Testen historischer Versionen beibehält. Das Ziel ist es, hg und das Subrepo-Modell, das bisher verwendet wurde, zurückzuziehen und sich zu einem einheitlichen Baum in git zu bewegen, ohne die Geschichte zu opfern.
Ich beginne mit einem Mercurial-Repository, das aus Code der obersten Ebene und einer Reihe von Unterlagern besteht, in denen der Großteil der interessanten Geschichte liegt. Die Subrepos haben einige Verzweigungen / Zusammenführungen, aber nichts zu verrückt. Das Endergebnis, das ich erreichen möchte, ist ein einzelnes Git Repository, ohne Submodule, so dass:
Für jeden Commit im ursprünglichen hg Repo der obersten Ebene gibt es einen git commit, der genau denselben Baum auscheckt, wie Sie den entsprechenden hg commit mit all seinen Referenzen subrepo commits auschecken würden.
Diese Git-Commits, die aufeinander folgenden hg-Commits auf oberster Ebene entsprechen, sind Abkömmlinge voneinander, wobei die Commits allen relevanten Subrepo-Commits dazwischen entsprechen.
Die grundlegende Idee, die ich habe, um dies zu erreichen, besteht darin, alle hg-Commits auf oberster Ebene zu durchlaufen und für jedes Top-Level-Commit, das .hgsubstate
ändert, auch über alle Pfade von der alten Revision zur neuen Revision zu iterieren für das Submodul (möglicherweise Verzweigung). Bei jedem Schritt:
git-write-tree
und git-commit-tree
, um ein Commit mit den gewünschten Eltern zu generieren, indem Sie die Autorenschaft, das Datum und die Commit-Nachricht aus dem entsprechenden hg commit verwenden. Sollte das funktionieren? Gibt es einen besseren Weg, um zu erreichen, was ich will, vielleicht den Subrepo-Kollaps mit hg zuerst zu machen? Das größte, was mir nicht klar ist, ist, wie man die gewünschte Iteration durchführt, also wäre ein praktischer Ratschlag, wie man es erreichen könnte, großartig.
Eine zusätzliche Einschränkung: Die Original-Repos beinhalten Inhalte, die nicht veröffentlicht werden können (dies ist ein zusätzlicher git-filter-branch
-Schritt, sobald die grundlegende Konvertierung durchgeführt wurde), so dass Lösungen, die das Hochladen des Repos zur Verarbeitung durch einen Dritten beinhalten, nicht praktikabel sind.
Was Sie geschrieben haben, könnte das Problem lösen oder nicht. Aber es ist nicht einfach. Das Hauptproblem besteht darin, dass Sie in Reihenfolge committen müssen, damit Ihre subrepos und main Repo konsistent sind. Ich habe dieses Problem in einem kleinen Maßstab neu erstellt und konnte Konsistenz zwischen Subrepos auch haben).
Meine Lösung:
Mit Hilfe von hg convert extension habe ich den Hauptrepo in einen Repo ohne Subrepos (und zugehörige Informationen) umgewandelt.
%Vor%Konvertiere Subrepo mit dem Umbenennen in --filemap.
%Vor%Ziehen Sie Subrepos zum konvertierten Hauptrepo.
%Vor%Sie werden während des Ziehens mehrere Köpfe im Repo bemerken (weil Subrepo ihren eigenen Kopf haben). Vereinigt sie:
%Vor%Sie müssen 3 & amp; 4 für jeden Subrepo.
Aber commits werden nicht sortiert. Also stell sie in Ordnung wie hier Ссылка :
%Vor%Konvertieren Sie dieses geordnete Quecksilber-Repo jetzt mit Ссылка in git:
%Vor% Ja. Am besten erstellen Sie die Commits manuell mit git commit-tree
. Es gibt viele Konvertierungstools, die Ihnen jedoch nie genau das geben, was Sie möchten. Auf der anderen Seite gibt Ihnen ein handgeschriebenes Skript die Flexibilität, die Sie brauchen.
Ich habe viele dieser Skripte geschrieben, einschließlich git remote-hg
selbst.
Nicht zusammenhängende Offtopik
Ich bin mir sicher, dass Sie die schlechteste Idee der Migration ausgewählt haben (von Mercurial zu Git), aber es ist Ihre Entscheidung und Ihre Verantwortung letztendlich
Migrationskurs
Mein Wissen über Git ist ziemlich schwach, also für Mercurial + subrepo - & gt; monolithisches Git kann ich nur so sehen und beschreiben:
Mercurial + subrepo - & gt; monolithisches Mercurial - & gt; monolithisches Git Repo
Das habe ich gemacht, um ein ähnliches Problem zu lösen:
git checkout -b
jedem Subrepos-Repository einen Namen git read-tree --prefix=pathsubrepo/ -u subrepobranch
für jeden Unterbericht Das ist mehr oder weniger das, was ich ein bisschen ausführlicher gemacht habe (angepasst aus bash history ... aber nicht wirklich ausgeführt)
Schritt 1
%Vor%Schritt 2
%Vor%Schritt 3
%Vor%Schritt 4
%Vor% Sobald Sie fertig sind, können Sie git branch -D sub1master
und git branch -D sub2master
verwenden, da Sie sie nicht mehr benötigen.
Es scheint mir, was ich bei meiner Frage und der Diskussion möglicher Lösungen vermisst habe, ein richtiges Verständnis der involvierten Graphentheorie. Ideen wie "Iterieren über alle Wege von der alten Revision zur neuen Revision" waren nicht wirklich gut definiert oder spiegelten zumindest nicht das wieder, was ich von ihnen erwartet hatte. Von einem strengeren Standpunkt aus gesehen, denke ich, dass ich einen Ansatz habe, der funktioniert.
Zunächst einmal das Problem: Subrepo-Revisionen repräsentieren nur den Zustand ihrer eigenen Teilbäume an einem bestimmten Punkt im Verlauf. Ich möchte sie Revisionen zuordnen, die den Zustand des gesamten kombinierten Baumes darstellen. Dann können die Subrepo DAG s sinnvoll mit der DAG der obersten Ebene zusammengeführt werden.
Für eine gegebene Subrepo-Revision R können wir fragen, welche Repo der obersten Ebene (oder Parent-Repo, wenn wir mehrere Ebenen von Subrepos hatten) R oder irgendeinen Nachkommen von R enthalten. Unter der Annahme einer einzelnen Wurzel würde diese Menge von Revisionen haben einen Niedrigsten gemeinsamen Vorfahren (oder vielleicht mehr als einen), der wie ein guter Kandidat aussieht. In der Tat, wenn die Top-Level-Revision S, die wir mit R verwenden, kein gemeinsamer Vorfahre von Revisionen ist, die R oder seine Nachkommen verwenden (aber die Zuordnung ist ansonsten vernünftig), dann wird R einen Nachkommen R 'haben, dessen zugehörige Top-Level-Revision S 'ist kein Nachkomme von S. Mit anderen Worten, die aus der Subrepo abgeleitete Geschichte wird verwirrende / unsinnige Sprünge zwischen Revisionen der obersten Baumstruktur haben.
Wenn wir jetzt einen gemeinsamen Vorfahren wählen wollen, macht der niedrigste Sinn unter dem Gesichtspunkt, diese Revisionen zu etwas zu machen, das ausgecheckt, gebaut und getestet werden kann, und vom Standpunkt, eine vernünftige Idee zu geben, was Der Status des Repos der obersten Ebene (und anderer Subrepos) war zu dem Zeitpunkt, zu dem die Änderungen im Subrepo vorgenommen wurden. Die Wurzel der gesamten DAG der obersten Ebene würde natürlich auch funktionieren, aber es würde keine sinnvollen, brauchbaren Überarbeitungen ergeben, die ausgecheckt werden könnten; Die Auswahl der Wurzel wäre von einem Usability-Standpunkt her äquivalent zu einer naiven Repomischung, die eine Wurzel pro Subrepo hat und nur aus den Subrepo-Historien zusammenfließt, wenn die Repo der obersten Ebene die von ihr verwendeten Revisionen aktualisiert.
Wenn wir also die LCA verwenden können, um jeder Subrepo-Revision R eine Top-Level-Revision T (R) zuzuordnen, wie übersetzt sich das dann in
?Immer wenn eine Subrepo-Revision R T (R) für jedes Elternteil P von R unterscheidet, werden neue Änderungen effektiv vom Repo der obersten Ebene (und anderen Subrepos) in die Subrepohistorie zusammengeführt. Die Konvertierung sollte dies als zwei Commits darstellen:
Das eigentliche Subrepo-Commit R, das eine alte Top-Level-Revision verwendet. Wenn R ein einziges Elternteil P hat (kein Merge-Commit), ist dies T (P). Wenn R mehrere Eltern hatte, ist es nicht klar, ob es eine perfekte Wahl gibt, welche zu verwenden ist, aber T (P) für irgendein Elternteil P sollte vernünftig sein.
Ein Zusammenführungs-Commit, das die Konvertierung C (T (R)) des mit R verknüpften Top-Level-Repo-Commits T (R) zusammenführt, wobei C (T (R)) selbst gerade verschmolzen ist (1) oben.
Abgesehen von C (T (R)), das (1) als ein Merge-Elternteil referenziert, sollten alle anderen Referenzen auf R in der Konvertierung (2) verwenden. Dies beinhaltet die Konvertierungen aller Nachkommen von T (R) im Repo der obersten Ebene, die die Revision R dieses Subrepo verwenden, und die Konvertierungen von direkten Kindern von R selbst.
Ich glaube, dass die obige (wenn auch schlecht formulierte) Beschreibung alles angibt, was zum Zusammenführen der Top-Level- und Subrepo-DAGs benötigt wird. Jede Subrepo-Revision erhält eine Vollversion der Struktur und wird über "Zusammenführungs-Commits" zu einer einheitlichen DAG für den konvertierten Repo verbunden (wenn der Subrepo eine neue zugehörige Top-Level-Revision zusammenführt und die Top-Level-Subrepo-Revisionen zusammenführt) das hat sich geändert).
Der letzte Schritt beim Erstellen des git-Repos besteht darin, einfach die zusammengefügte DAG entweder in topologisch sortierter Form oder über einen Tiefenanlauf abzuspielen, so dass jedes git commit-tree
bereits alle Elternrevisionen hat, die es benötigt.
Versuchen Sie es mit Facebooks Hg & lt; - & gt; Git-Konverter: FbShipIt
. Das meiste von dem, was Sie beschrieben haben, sollte gut mit diesem Commit Converter-Tool funktionieren, das die Commits zwischen Mercurial und Git kopiert.
FbShipIt
hat einen Vorbehalt: Es versteht Merge Commits nicht, aber es kann über git rebase
umgearbeitet werden .
Tags und Links git mercurial version-control mercurial-subrepos